AdSense

Showing posts with label Dynamic Programming. Show all posts
Showing posts with label Dynamic Programming. Show all posts

Monday, October 3, 2016

Maximal Square

Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.
For example, given the following matrix:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Return 4.


This is actually a DP problem. For any point, to form a larger square, it's left, upper left, upper point should also be part of the square, so maximal length is the minimum of the three, if current point is 1. If current point is 0, it can not form any square.


public int maximalSquare(char[][] matrix) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0)
            return 0;
        int m = matrix.length;
        int n = matrix[0].length;
        int[][] len = new int[m][n];
        int maxLen = 0;
        for (int i = 0; i < m; i++) {
            len[i][0] = matrix[i][0] - '0';
            maxLen = Math.max(maxLen, len[i][0]);
        }
            
        
        for (int j = 0; j < n; j++) {
            len[0][j] = matrix[0][j] - '0';
            maxLen = Math.max(maxLen, len[0][j]);
        }
            
        
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                len[i][j] = matrix[i][j] == '1' ? Math.min(len[i][j - 1], Math.min(len[i - 1][j - 1], len[i - 1][j])) + 1
                : 0;
                maxLen = Math.max(maxLen, len[i][j]);
            }
        }
        return maxLen * maxLen;
    }


Friday, August 19, 2016

Combination Sum IV

Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.
Example:
nums = [1, 2, 3]
target = 4

The possible combination ways are:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)

Note that different sequences are counted as different combinations.

Therefore the output is 7.



The question asks about how many ways to get the sum. This doesn't need to use backtracking. DP is a better solution. For any amount i, if a number n is less than the current amount, the current amount can be acquired by (i - n + n), thus number of ways to get current amount is incremented by dp[i - n].

public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target + 1];
        dp[0] = 1;
        
        for (int i = 1; i <= target; i++) {
            for (int n : nums) {
                if (i >= n) {
                    dp[i] += dp[i - n];
                }
            }
        }
        return dp[target];
        
    }

Monday, June 27, 2016

Coin Change

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
Example 1:
coins = [1, 2, 5], amount = 11
return 3 (11 = 5 + 5 + 1)
Example 2:
coins = [2], amount = 3
return -1.
Note:
You may assume that you have an infinite number of each kind of coin.

This is a dp problem. For each amount, find the possible combinations from the coins, return if the amount can be formed.


public int coinChange(int[] coins, int amount) {
        if (coins == null || coins.length == 0)
            return -1;
        int[] combinations = new int[amount + 1];
        Arrays.fill(combinations, 1, amount + 1, Integer.MAX_VALUE);
        
        for (int i = 0; i <= amount; i++) {
            for (int j = 0; j < coins.length; j++) {
                if (i + coins[j] <= amount && combinations[i] != Integer.MAX_VALUE) {
                    combinations[i + coins[j]] = Math.min(combinations[i] + 1, combinations[i + coins[j]]);
                }
            }
        }
        return combinations[amount] != Integer.MAX_VALUE ? combinations[amount] : -1;
    }


Friday, March 27, 2015

House coloring problem

I ask my friends to throw me a practicing problem, here it is:

There are a row of houses, each house can be painted with three colors red, blue and green. The cost of painting each house with a certain color is different. You have to paint all the houses such that no two adjacent houses have the same color. You have to paint the houses with minimum cost. How would you do it?

No doubt the first thought is DP. However, the trick is we cannot only create an array of costs because we cannot determine which color to print except for the first house. 

The idea is:
cost[color0][house] = Math.min(cost[color1][house - 1], cost[color2][house - 2]) + houseCost[color0][house]. 

Now here is a follow up question: what if there are n colors? I don't know a good answer because all I can think of is to go through all cost of print different colors of house - 1 and find the minimum. But that will make the whole complexity O(mn), where m is the number of houses and n is the number of colors. 


public class HouseColoring {
 //assume rows are the cost of each color and columns are for each house
 public static int minCost(int[][] house){
  if(house == null || house.length == 0 ||house[0].length == 0)
   return -1;
  int cols = house[0].length;
  int[][] cost = new int[3][cols];
  for(int i = 0; i < 3; i++)
   cost[i][0] = house[i][0];
  for(int j = 1; j < cols; j++){
   cost[0][j] = Math.min(cost[1][j - 1], cost[2][j - 1]) + house[0][j];
   cost[1][j] = Math.min(cost[0][j - 1], cost[2][j - 1]) + house[1][j];
   cost[2][j] = Math.min(cost[0][j - 1], cost[2][j - 1]) + house[2][j];
  }
  return Math.min(cost[0][cols - 1], Math.min(cost[1][cols - 1], cost[2][cols - 1]));
 }
 public static void main(String[] args) {
  int[][] house = new int[3][];
  house[0] = new int[] {1, 3, 2, 6, 7, 8, 9};
  house[1] = new int[] {5, 4, 1, 3, 9, 8, 10};
  house[2] = new int[] {7, 6, 1, 5, 8, 2, 3};
  System.out.println(minCost(house));
 }
}

Thursday, March 26, 2015

Snakes and Ladders


Design an optimized algorithm to solve snakes and ladders with the least amount of roles possible under the assumption that you get whatever role you want when you role the dice.
I didn't even know about the game at first. As usual, if the question asks about "minimum" something, there is highly chance that it is a DP.

Just to describe the game for short:

It is a common board game, where you roll the dice and proceed the amount you get;
If you hit the lower side of the ladder, you are lucky, you move up to the upper side of the ladder;
If you face a snake (upper side of the snake), congratulations, go back to the tail (lower side) of the snake;
If you are the first one to proceed to the destination, you win.

If you are so interested in playing the game, consult Wikipedia.

Ok, back to the problem. Create a 1D array of length n * n, which is the minimum roles needed to proceed to that spot. If index < 6, then as long as it's not the upper side of the snake, it takes minimum 1 step. If index > 6, then it takes minimum from index - 6 to index - 1 plus 1 step, assuming normal condition. If it is the upper side of the ladder, it equals the step needed to go to the lower side of the ladder. If it is the upper side of a snake, since every time you hit this square, you always have to return, it takes infinity to go to the destination.

I use a struct to store the information on the board. The board of the test case can be viewed as this:





import java.util.*;
public class SnakesNLadders {
 public static int roles(Struct[][] board){
  if(board == null || board.length == 0 || board[0].length == 0 || board.length != board[0].length)
   return 0;
  int n = board.length;
  int[] roles = new int[n * n];
  for(int i = 0; i < n * n; i++){
   int x = i / n;
   int y = (x % 2 != 0) ? (n - 1 - i % n) : i % n;
   if(i < 6){
    roles[i] = board[x][y].s.equals("SU") ? Integer.MAX_VALUE / 2 : 1;
   }
   else{
    roles[i] = Integer.MAX_VALUE;
    for(int j = i - 6; j < i; j++)
     roles[i] = Math.min(roles[i], roles[j] + 1);
    if(board[x][y].s.equals("LU"))
     //in case the lower end of the ladder is the upper end of the
     //snake
     roles[i] = Math.min(ladder(board, x, y, roles),  roles[i]);
    else if(board[x][y].s.equals("SU"))
     roles[i] = Integer.MAX_VALUE / 2;
   }
  }
  
  return roles[n * n - 1];
 }
 private static int ladder(Struct[][] board, int x, int y, int[] roles){
  int n = board.length;
  int xc = board[x][y].x;
  return xc % 2 != 0 ? roles[xc * n + (n - 1 - board[x][y].y)]
     : roles[xc * n + board[x][y].y];
 }
 /**
  * consists the string which represents the status of the square:
  * "SU": snake upper side
  * "SL": snake lower side
  * "LU": ladder upper side
  * "LL": ladder lower side
  * as well as the coordinates of its corresponding end
  * e.g., "SU", 1, 1 indicates the lower side of the snake is at x = 1, y = 1 
  * @author shirleyyoung
  *
  */
 private static class Struct{
  String s;
  //the coordinate of the corresponding square
  int x;
  int y;
  public Struct(String s, int x, int y){
   this.s = s;
   this.x = x;
   this.y = y;
  }
 }
 public static void main(String[] args) {
  Struct[][] board = new Struct[4][4];
  for(int i = 0; i < 4; i++)
   //indicate nothing in the square
   Arrays.fill(board[i], new Struct("", -1, -1));
  board[0][1] = new Struct("SL", 1, 2);
  board[1][2] = new Struct("SU", 0, 1);
  board[0][3] = new Struct("LL", 2, 3);
  board[2][3] = new Struct("LU", 0, 3);
  board[1][1] = new Struct("SL", 3, 2);
  board[3][2] = new Struct("SU", 1, 1);
  board[1][0] = new Struct("LL", 2, 0);
  board[2][0] = new Struct("LU", 1, 0);
  System.out.println(roles(board));
 }
}

Tuesday, March 17, 2015

Best time to buy and sell stork - IV


It took me a while to figure out the DP solution.
At each day, either it trades or it doesn't , the maximum profit will be between profits[i][j - 1] and the profit gain from selling at prices[j]. Now we need to the determine the maximum profit before we sell at prices[j], and that comes from transition i - 1 (tmpMax).
Consider we are at transition i - 1 and we just get our maximum profit, now we buy at prices[j] (not the same j as in the last paragraph), the maximum profit reduces to profit[i - 1][j] - prices[j], we compare this profit with the previous tmpMax, if it is smaller than tmpMax, we will not buy the stock at day j and leave the tmpMax as it it.

Since at maximum we can trade prices.length / 2 times, if k is larger than that, we simply use the solution in Best time to buy and sell stork - II to solve the problem.

Update 2016-09-26

Max profit of the day is price at the day - minimum cost of last day if we sell the stock today or last day's profit if we don't.
Minimum cost of the day is the minimum cost of last day if we don't buy the stock or today's price - maximum profit of last transaction if we buy the stock today.

public int maxProfit(int k, int[] prices) {
        if(prices == null || prices.length == 0)
            return 0;
        int len = prices.length;
        if(k >= len / 2)
            return quickSolve(prices);
        int[][] profits = new int[k + 1][len];
        for(int i = 1; i <= k; i++){
            int tmpMax = -prices[0];
            for(int j = 1; j < len; j++){
                profits[i][j] = Math.max(profits[i][j - 1], prices[j] + tmpMax);
                tmpMax = Math.max(tmpMax, profits[i - 1][j] - prices[j]);
            }
        }
        return profits[k][len - 1];
    }
    public int quickSolve(int[] prices){
        int profit = 0;
        for(int i = 1; i < prices.length; i++){
            profit += prices[i] - prices[i - 1] > 0 ? prices[i] - prices[i - 1] : 0;
        }
        return profit;
    }

Sunday, March 15, 2015

Diameter of a graph


Problem Statement
Diameter
The diameter of a graph is the maximum shortest path between any two nodes.
At the beginning, there is a simple grpah contains exactly 1 node. Then we add new nodes one by one to the graph. Each time when we add a new node to the graph, we also add exactly one edge to connect this node to another node which already exists.
We want to find the diameter of the graph each time we add a new node. Note that each edge cost 1.
Input Format:
First line of the input contains one integer N, indicating how many new nodes we will add.
Then following N lines. The ith line contains an integer X, which means we add the ith node and an edge connecting the Xth node and ith node.
The original node is the 0th node.
Output Format:
Output N lines. The ith line is an integer indicating the diameter of the graph after adding the ith node.
Constraints:
0 < N <= 100000
0 <= Xi < i
i is counting from 1
Sample Input:
5
0
0
1
1
1
Sample Output:
1
2
3
3
3
Explanation:
Firstly the graph contains only node 0. The first line of output is 1 because the diameter becomes 1 when node 1 is added and connected to node 0. Diameter becomes 2 after adding node 2 to node 0. Then adding node 3, 4, 5, all of them are connecting to node 1, caculate the shortest path of all pairs of nodes, the maximum shortest path is 3, so the last 3 lines of output are all 3.



My approach is based on the assumption that


  1. The constructed graph is a tree
  2. The shortest distance between any node pair will not change when the new node is added. 


Somehow I think my approach is correct, but I don't know how to prove it.


public class Diameter {
 static int N;
 static int[][] shortestPath;
 static int newNode = 0;
 public static void maxShortestDistance(String input){
  Stdin inStream = new Stdin(input);
  N = Integer.parseInt(inStream.readLine());
  shortestPath = new int[N + 1][N + 1];
  for(int i = 0; i <= N; i++)
   //Integer.MAX_VALUE will overflow
   Arrays.fill(shortestPath[i], Integer.MAX_VALUE / 2);
  for(int i = 0; i <= N; i++)
   shortestPath[i][i] = 0;
  int max = 0;
  try{
   while(!inStream.isEmpty()){
    newNode++;
    int connected = Integer.parseInt(inStream.readLine());
    shortestPath[connected][newNode] = 1;
    shortestPath[newNode][connected] = 1;
    for(int i = 0; i < newNode; i++){
     for(int j = 0; j < newNode; j++){
      shortestPath[i][newNode] = Math.min(shortestPath[i][newNode], 
        shortestPath[i][j] + shortestPath[j][newNode]);
     }
     shortestPath[newNode][i] = shortestPath[i][newNode];
     max = Math.max(max, shortestPath[i][newNode]);
     //System.out.println(i + " to " + newNode + ": " + shortestPath[newNode][i]);
    }
    System.out.println(max);
   }
  } catch (Exception e){
   System.out.println(e);
  }
  
  inStream.close();
 }

 public static void main(String[] args) {
  maxShortestDistance("/Users/shirleyyoung/Documents/workspace/Google/src/maxShortestDistance.txt");
 }
}

Monday, January 26, 2015

Knapsack

This is the first DP algorithm I learned when I started learning algorithm at the first time. I was so stupid back then that I didn't know what the matrix really means, so I literally wrote a code and "calculated" the matrix.

Uh, those time...

Ok, back to the problem.

The version of the question I learned back then is that a thief is breaking into a museum. He has a bag that can carry at most weight W. There are n items in the museum[0, ...i, ... n - 1]. Each is weighted w_i and has value v_i. None of the items can be broken down to pieces. The thief needs to decide which items to carry in order to get the maximum value.

Yeah, we are trying to help a crime... -_-

As we mentioned, the solution is a DP approach. So how to build the matrix?
If we break down the problems to 1 item and weight w, then if the item is heavier than weight, the thief cannot carry it, then the remaining weight will be w - w_1. Let's assume we can carry the item 1. Now if we add one more item, we either choose to carry the item 2, and the remaining weight will be w _ w_1 - w_2, and the value will be v_1 + v_2. Or we choose not to carry it, then there is only item 1 in the bag. Yup, it is Max(bag[i - 1][w], bag[i - 1][w - w_[i - 1]] + v[i - 1]).


public static int maxValue(int[] value, int[] weight, int maxW) {
  if (value == null || weight == null || value.length != weight.length)
   throw new NullPointerException("Invalid input!");
  int[][] values = new int[value.length + 1][maxW + 1];
  for (int i = 1; i <= value.length; i++) {
   for (int w = 1; w <= maxW; w++) {
    if (weight[i - 1] <= w) {
     values[i][w] = Math.max(values[i - 1][w], values[i - 1][w - weight[i - 1]] + value[i - 1]);
    }
    else
     values[i][w] = values[i - 1][w];
   }
  }
  return values[value.length][maxW];
 }

Saturday, January 17, 2015

Longest Common Subsequence

Very typical 2D DP.


public static int longestCommonSubsequence (String a, String b) {
  if (a == null || b == null || a.length() == 0 || b.length() == 0)
   return 0;
  int len1 = a.length();
  int len2 = b.length();
  int[][] lcs = new int[len1 + 1][len2 + 1];
  lcs[0][0] = 0;
  for (int j = 0; j <= len2; j++) 
   lcs[0][j] = 0;
  for (int i = 0; i <= len1; i++) 
   lcs[i][0] = 0;
  for (int i = 1; i <= len1; i++) {
   for (int j = 1; j <= len2; j++) {
    if (a.charAt(i - 1) == b.charAt(j - 1)) {
     lcs[i][j] = Math.max(Math.max(lcs[i - 1][j], lcs[i][j - 1]), lcs[i - 1][j - 1] + 1);
    }
    else {
     lcs[i][j] = Math.max(Math.max(lcs[i - 1][j], lcs[i][j - 1]), lcs[i - 1][j - 1]);
    }
   }
  }
  return lcs[len1][len2];
 }

Wednesday, January 7, 2015

Dungeon Game

The demons had captured the princess (P) and imprisoned her in the bottom-right corner of a dungeon. The dungeon consists of M x N rooms laid out in a 2D grid. Our valiant knight (K) was initially positioned in the top-left room and must fight his way through the dungeon to rescue the princess. 
The knight has an initial health point represented by a positive integer. If at any point his health point drops to 0 or below, he dies immediately. 
Some of the rooms are guarded by demons, so the knight loses health (negative integers) upon entering these rooms; other rooms are either empty (0's) or contain magic orbs that increase the knight's health (positive integers).
In order to reach the princess as quickly as possible, the knight decides to move only rightward or downward in each step. 

Write a function to determine the knight's minimum initial health so that he is able to rescue the princess.
For example, given the dungeon below, the initial health of the knight must be at least 7 if he follows the optimal path RIGHT-> RIGHT -> DOWN -> DOWN.
-2 (K)-33
-5-101
1030-5 (P)

Notes:
  • The knight's health has no upper bound.
  • Any room can contain threats or power-ups, even the first room the knight enters and the bottom-right room where the princess is imprisoned.

A new LeetCode problem. 2D matrix, minimumHP, naturally, that is DP. The interesting part here is instead of solving from top-left, this time we need to solve from bottom right.

In the room where the princess(p) is imprisoned, the knight(k) needs at least 1 HP. As the note suggests, "Any room can contain threats or power-ups", so the minimumHP needed is 1 + dungeon[m -1][n - 1]. What if the room contains power-ups? That means the minimumHP k needs in that room is 1.

Note that if we need more HP, we need to add the points that will be deducted when fighting with the demons. And since that point is negative, we need to deduct that point. If there is a power-up, that number will be negative, and we take the minimum point needed, which is 1. Take the bottom right room as the example:
 minHP[i][n - 1] = Math.max(minHP[i + 1][n - 1] - dungeon[i][n - 1], 1);

The boundary can be calculated by deducting dungeon[m - 1][j] (dungeon[i][n - 1]) from minHP[m - 1][j + 1] (minHP[ i + 1][n - 1]) and taking the minimum between the result and 1.

For the rooms in the middle, since k can only go right and down, we take the minimum between minHP[i + 1][j] and minHP[i][j + 1], i.e., get the minimum HP in order to enter the next room.

minHP[i][j] = Math.min(minHP[i + 1][j], minHP[i][j + 1]) - dungeon[i][j];
minHP[i][j] = (minHP[i][j] <= 0) ? 1 : minHP[i][j];

When we reach minHP[0][0], we get the minimum HP needed for k.


public int calculateMinimumHP(int[][] dungeon) {
        if (dungeon == null)
            throw new NullPointerException("Null dungeon...!");
        if (dungeon.length == 0 || dungeon[0].length == 0) {
            System.out.println("Well, apparently there is no threat...");
            return 0;
        }
        int m = dungeon.length;
        int n = dungeon[0].length;
        int[][] minHP = new int[m][n];
        minHP[m - 1][n - 1] = Math.max(-dungeon[m - 1][n - 1] + 1, 1);
        for (int i = m - 2; i >= 0; i--) {
            minHP[i][n - 1] = Math.max(minHP[i + 1][n - 1] - dungeon[i][n - 1], 1);
        }
        for (int j = n - 2; j >= 0; j--) {
            minHP[m - 1][j] = Math.max(minHP[m - 1][j + 1] - dungeon[m - 1][j], 1);
        }
        for  (int i = m - 2; i >= 0; i--) {
            for (int j = n - 2; j >= 0; j--) {
                minHP[i][j] = Math.min(minHP[i + 1][j], minHP[i][j + 1]) - dungeon[i][j];
                minHP[i][j] = (minHP[i][j] <= 0) ? 1 : minHP[i][j];
            }
        }
        return minHP[0][0];
        
    }

I never like games, no, never...




Wednesday, December 24, 2014

Longest palindromic subsequence of an array

Write a function to compute the maximum length palindromic sub-sequence of an array. 
A palindrome is a sequence which is equal to its reverse. 
A sub-sequence of an array is a sequence which can be constructed by removing elements of the array. 
Ex: Given [4,1,2,3,4,5,6,5,4,3,4,4,4,4,4,4,4] should return 10 (all 4's) 


Well, I think my whole Christmas eve is filled with DP, palindromes and strings (this one is sort of one...). This is a Linkedin interview question. The solution, as far as I can think of, is DP (again). I had some problem figuring out how to construct the DP matrix. It is an upper (or lower) diagonal matrix with the rows as the start character and columns as the end characters.

Here is a sample matrix, I use string instead of those annoying numbers.
S = "BBABA"



public class PalindromicSubsequence {
 public int maxLengthPalindrome(int[] nums) {
  if (nums == null || nums.length == 0) 
   return 0;
  int[][] palindrome = new int[nums.length][nums.length];
  for (int i = 0; i < nums.length; i++) {
   palindrome[i][i] = 1;
  }
  for (int i = 0; i < nums.length - 1; i++) {
   palindrome[i][i + 1] = nums[i] == nums[i + 1] ? 2 : 1;
  }
  for (int len = 3; len <= nums.length; len++) {
   for (int start = 0; start <= nums.length - len; start++) {
    int end = start + len - 1;
    palindrome[start][end] = Math.max(palindrome[start][end - 1], palindrome[start + 1][end]);
    if (nums[start] == nums[end]) {
     palindrome[start][end] = Math.max(palindrome[start][end], palindrome[start + 1][end - 1] + 2);
    }
   }
  }
  return palindrome[0][nums.length - 1]; 
 }
}

Longest Palindromic Substring - DP & O(n) solution

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.

The first thought when seeing keyword "longest" and "substring" would be DP! Yep, and the solution is accepted.




public class LongestPalindrome {
    public String longestPalindrome(String s) {
     if (s == null || s.length() == 0)
      return "";
     String rst = s.substring(0,1);
     int maxSubstring = 1;
     boolean[][] palindrome = new boolean[s.length()][s.length()];
     for (int i = 0; i < s.length(); i++) {
      palindrome[i][i] = true;
     }
     for (int len = 1; len < s.length(); len++) {
      for (int i = 0; i + len < s.length() ; i++) {
       if (len < 2) {
        palindrome[i][i + len] = (s.charAt(i) == s.charAt(i + len));
       }
       else {
        palindrome[i][i + len] = palindrome[i + 1][i + len - 1] && (s.charAt(i) == s.charAt(i + len));
       }
       if (palindrome[i][i + len] && (len + 1 > maxSubstring)){
        maxSubstring = len + 1;
        rst = s.substring(i, i + len + 1);
       }
      }
     }
     return rst;
    }
}

However, as we know, 2D DP requires O(n^2) complexity. Naturally we will ask, can we do better?
Of course we can! Otherwise what's this post about?

The complete explanation of this O(n) solution, which is called, Manacher's Algorithm can be found here. I will just simplify it based on my understanding.

So consider we have a string s = "aabab". How can we check every substring using iteration? We need to check "aa", "aab", "aaba", "aabab", then "aba" ... and so on. Then this will be O(n^2), we are not doing anything better. But, what if we add something into the string:

# a # a # b # a # b #
0   1  2  3  4   5  6   7  8   9  10

Well, whatever symbol you would like to use is fine. The point is, now we double the length of the string, and every substring of s is symmetric in the new string. If we want to check "aa", it is symmetric against "#", "aba" is symmetric against "b". Thus, by iterate through the new string, we can check every substring of s in linear time.


public class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() == 0)
            return "";
        int maxSubstring = 1;
        String rst = s.substring(0, 1);
        for (int i = 1; i <= 2 * s.length() - 1; i++) {
            int count = 1;
            while (i - count >=  0 && i + count <= 2 * s.length() && get(s, i - count) == get(s, i + count)) {
                count++;
            }
            //Note that since "#" always equals "#", we will have an extra count for each substring
            count--;
            if (count > maxSubstring) {
                maxSubstring = count;
                rst = s.substring((i - count) / 2, (i + count) / 2);
            }
        }
        return rst;
    }
        private char get(String s, int index) {
            if (index % 2 == 0)
                return '#';
            else
                return s.charAt(index / 2);
        }
}

Ah, beautiful solution! :)

Monday, December 22, 2014

One Edit Distance & Edit Distance

Given two strings S and T, determine if they are both one edit distance apart. 
Hint:
1. If | n – m | is greater than 1, we know immediately both are not one-edit distance apart.
2. It might help if you consider these cases separately, m == n and m ≠ n.
3. Assume that m is always ≤ n, which greatly simplifies the conditional statements. If m > n, we could just simply swap S and T.
4. If m == n, it becomes finding if there is exactly one modified operation. If m ≠ n, you do not have to consider the delete operation. Just consider the insert operation in T.
From Wikipedia:
In computer scienceedit distance is a way of quantifying how dissimilar two strings (e.g., words) are to one another by counting the minimum number of operations required to transform one string into the other.

 Separate the case that S and T have different lengths and have same lengths. Consider some corner cases.
Note since edit distance only allow Insertion, Deletion, and Substitution, the following strings are not one edit distance.

S = " fdgvf "
T = " dfgvf "


public class OneEditDistance {
 public boolean isOneEditDistance(String s, String t)
 {
  if ((s == null && t != null) || (t == null && s != null))
   return false;
  if (s == null && t == null)
   return true;
  if (s.equals(t))
   return true;
  if (Math.abs(t.length() - s.length() )> 1)
   return false;
  if (t.length() == s.length())
   return isOneEditSameLength(s, t);
  return isOneEditDiffLength(s, t);
 }
 private boolean isOneEditSameLength(String s, String t)
 {
  int diff = 0;
  for (int i = 0; i < s.length(); i++)
  {
   if(s.charAt(i) != t.charAt(i))
    diff++;
   if (diff > 1)
    return false;
  }
  return true;
 }
 private boolean isOneEditDiffLength(String s, String t)
 {
  if (s.length() > t.length())
  {
   String tmp = s;
   s = t;
   t = tmp;
  }
  int index = 0;
  while (index < s.length() && s.charAt(index) == t.charAt(index))
  {
   System.out.println(index);
   index++;
  }
  if (index == s.length())
   return true;
  return s.substring(index).equals(t.substring(index + 1));
 }
 
}

Moreover, I find it's helpful to also post this classic DP algorithm to calculate the edit distance of two strings.


public class EditDistance {
    public int minDistance(String word1, String word2) {
        if (word1 == null || word2 == null)
            throw new Error("Null string(s)");
        
        int[][] distance = new int[word1.length() + 1][word2.length() + 1];
        for (int i = 0; i < distance.length; i++)
            distance[i][0] = i;
        for (int j = 1; j < distance[0].length; j++)
            distance[0][j] = j;
        for (int i = 1; i < distance.length; i++)
        {
            for (int j = 1; j < distance[0].length; j++)
            {
                if (word1.charAt(i - 1) == word2.charAt(j - 1))
                    distance[i][j] = distance[i - 1][j - 1];
                else
                {
                    distance[i][j] = Math.min(distance[i - 1][j], distance[i][j - 1]) + 1;
                    distance[i][j] = Math.min(distance[i][j], distance[i - 1][j - 1] + 1);
                }
            }
        }
        return distance[word1.length()][word2.length()];
    }
}

Friday, December 19, 2014

Interleaving String

Given s1s2s3, find whether s3 is formed by the interleaving of s1 and s2.
For example,
Given:
s1 = "aabcc",
s2 = "dbbca",
When s3 = "aadbbcbcac", return true.
When s3 = "aadbbbaccc", return false.
Two Dimensional DP.
First, if the length of s3 doesn't equal the sum of the lengths of s1 and s2, return false.
I also checked the situation where s1 or s2 is empty.

Consider any substrings of s1.substring(0, i), s2.substring(0, j) and s3.substring(0, i + j).
If s3.charAt(i + j - 1) == s1.charAt(i), then s3.substring(0, i + j - 1) should be an interleaving of s1.substring(0, i - 1) and s2.substring(0, j). Similarly, if s3.charAt(i + j - 1) == s2.charAt(j), then s3.substring(0, i + j - 1) should be an interleaving of s1.substring(0, i) and s2.substring(0, j - 1).


public class InterLeave {
    public boolean isInterleave(String s1, String s2, String s3) {
        if (s1 == null || s1.length() == 0)
            return s3.equals(s2);
        if (s2 == null || s2.length() == 0)
            return s3.equals(s1);
        if (s1.length() + s2.length() != s3.length())
            return false;
        boolean[][] interleave = new boolean[s1.length() + 1][s2.length() + 1];
        interleave[0][0] = true;
        for (int i = 1; i <= s1.length(); i++)
        {
            if (s3.charAt(i - 1) == s1.charAt(i - 1) && interleave[i - 1][0])
                interleave[i][0] = true;
        }
        for (int j = 1; j <= s2.length(); j++)
        {
            if (s3.charAt(j - 1) == s2.charAt(j - 1) && interleave[0][j - 1])
                interleave[0][j] = true;
        }
        for (int i = 1; i <= s1.length(); i++)
        {
            for (int j = 1; j <= s2.length(); j++)
            {
                if(s3.charAt(i + j - 1) == s1.charAt(i - 1) && interleave[i - 1][j] 
                || s3.charAt(i + j - 1) == s2.charAt(j - 1) && interleave[i][j - 1])
                    interleave[i][j] = true;
            }
        }
        return interleave[s1.length()][s2.length()];
        
    }
}

Wednesday, December 17, 2014

Word Ladder I & II

The first one:
Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:
  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Note:
  • Return 0 if there is no such transformation sequence.
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.

A typical BFS problem. Start from "start",  we go through all possible replacements and see if we can find some transformations in the dict. If we can, we put the intermediate word in the queue. The queue always stores transformations of words in the current level. (e.g, "dot" and "lot" will both be placed in the queue after "hot" is polled). Whenever "end" is reached, we return the length + 1, where "+ 1" indicates the "end" string.

public class Solution {
    public int ladderLength(String start, String end, Set dict) {
        if (dict == null || dict.size() == 0)
            return 0;
        if (start.equals(end))
            return 1;
        int length = 1;
        Queue queue = new LinkedList ();
        queue.offer(start);
        dict.remove(start);
        while (!queue.isEmpty())
        {
            //for multiple transformations, we only want to increment length once, 
            // thus only increment length after current stage of transformation is done
            int size = queue.size();
            for (int j = 0; j < size; j++)
            {
                String current = queue.poll();
                for (char c = 'a'; c <= 'z'; c++)
                {
                    for (int i = 0; i < current.length(); i++)
                    {
                        if (current.charAt(i) == c)
                            continue;
                        String tmp = replace(current, i, c);
                        if (tmp.equals(end))
                                return length + 1;
                        if (dict.contains(tmp))
                        {
                            //it is possible that another replacement of the character will lead to a shorter length, 
                            //thus length cannot be incremented here
                            //e.g., "a", "b", "c", if length is incremented at b, we will have one extra count
                            queue.offer(tmp);
                            dict.remove(tmp);
                        }
                    }
                }
            }
            //if no transformation is found, queue is empty, and we will exit the loop and return 0
            // otherwise we assume one transformation is found
            length++;
        }
        return 0;
    }

    private String replace(String s, int pos, char character)
    {
        char[] current = s.toCharArray();
        current[pos] = character;
        return new String(current);
    }
}


The second one:
Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:
  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
Return
  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]
Note:
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.
This problem is hard one because of the time and memory complexity.
Word Ladder I is a typical BFS problem, so for this one, intuitively we would think of using BFS too.
Not yet done...
Because we need to return every possible shortest path, we cannot simply remove the used words ("hot" is used in both paths). A possible approach can be as follows:

1. Do BFS first.
    Use a map<String, ArrayList<String>> to track all possible transformations to the key String. e.g., "cog", {"dog", "log"};
    Use another map<String, Integer> to track the level of the string. e.g., "hot", 1; "cog", 5;
2. The transformation is achieved by the same method as we used in the first problem.
3. Do DFS.
    From the "end" string, search the map and get the previous strings, until the start string is reached.
    Reverse the path (because we search from the end to the start) and return the path.

Note: Difference between Backtracking and DFS. 
Backtracking is a more general purpose algorithm. It can be used on any type of structure where portions of the domain can be eliminated.
DFS is a specific form of backtracking related to searching tree structures, and is limited to a tree structure.


public class Solution {
    public ArrayList> findLadders(String start, String end, Set dict) {
        ArrayList> ladders = new ArrayList> ();
        if (start.equals(end))
            return ladders;
        HashMap> prevWord = new HashMap>();
        HashMap level = new HashMap ();
        
        dict.add(start);
        dict.add(end);
        
        getTransformation(prevWord, level, start, dict);
        
        ArrayList path = new ArrayList();
        
        getPath(ladders, path, prevWord, level, end, start);
        
        return ladders;
        
    }
    //apply DFS
    private void getPath(ArrayList> ladders, ArrayList path, 
    HashMap> prevWord, HashMap level, String curr, String start)
    {
        path.add(curr);
        if (curr.equals(start))
        {
            Collections.reverse(path);
            ladders.add(new ArrayList(path));
            Collections.reverse(path);
        }
        else
        {
            for (String word : prevWord.get(curr))
            {
                if (level.containsKey(word) && level.get(curr) == level.get(word) + 1)
                    getPath(ladders, path, prevWord, level, word, start);
            }
        }
        // should be outside the loop since we add the word at the beginning of the method
        path.remove(path.size() - 1);
    }
    //applying BFS
    private void getTransformation(HashMap> prevWord, HashMap level,
    String start, Set dict)
    {
        Queue queue = new LinkedList();
        queue.offer(start);
        for (String s : dict)
            prevWord.put(s, new ArrayList());
        level.put(start, 0);
        while (!queue.isEmpty())
        {
            String curr = queue.poll();
            ArrayList list = transformWord(curr, dict);
            for (String word : list)
            {
                //prevWord tracks the word before 
             //the key is the word transformed from the value
             //curr is the word before the next
                prevWord.get(word).add(curr);
                //if level already contains the word, it means the word is at the upper level 
                //we do not want to change it to a lower level
                if (!level.containsKey(word))
                {
                    level.put(word, level.get(curr) + 1);
                    queue.offer(word);
                }
            }
        }
    }
    private ArrayList transformWord(String s, Set dict)
    {
        ArrayList transformations = new ArrayList();
        for (char c = 'a'; c <= 'z'; c++)
        {
            for (int i = 0; i < s.length(); i++)
            {
                if(s.charAt(i) == c)
                    continue;
                String tmp = s.substring(0,i) + c + s.substring(i + 1);
                if (dict.contains(tmp))
                    transformations.add(tmp);
            }
        }
        return transformations;
    }
    
}

Tuesday, December 16, 2014

Best Time to Buy and Sell Stock

Actually, it's never the best time!

Just kidding.

One key point needs to be remembered is that we have to buy in advance before sell (no short trading!). Thus we need to find the minimum element "before" the maximum value.

The first one:
Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.
For this problem, we only need to track the minimum element before the i-th element and let the temporary profit = prices[i] - min. Compare with the exist profit and find the maximum profit.


public class MaxProfit {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0)
            return 0;
       // int min = Integer.MAX_VALUE;
        int min = Integer.MAX_VALUE;
        int profit = 0;
        
        for (int i = 0; i < prices.length; i++)
        {
            min = prices[i] < min ? prices[i] : min;
            profit = (prices[i] - min) > profit ? (prices[i] - min) : profit;
        }
        
        return profit;
    }
}

The second one:
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

This one is actually even easier. Because there is no limit to the number of transactions (algorithmic trading? ), every time the i-th element is larger than (i - 1)-th element, we can complete the transaction.


public class MaxProfit {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0)
            return 0;
        int diff = 0;
        int profit = 0;
        for (int i = 1; i < prices.length; i++)
        {
            diff = prices[i] - prices[i - 1];
            if (diff > 0)
                profit += diff;
        }
        return profit;
    }
}

The third one (finally! I am so sleepy....-_-):
This one we need to apply DP. Not once, but twice. Given an array of prices, because there are two transactions, we need to find the maximum difference before i-th price and the maximum difference after the i-th price. So we do it twice. First from the beginning of the prices, and second from the end of the prices.

For example,

Given an array:
2, 6, 7, 9, 3, 2, 4, 8

We first compute the maximum profit we can get before the i-th price, using 1D DP, (refer to the first problem).

maxProfit1st:
0, 4, 5, 7, 7, 7, 7, 7

Second, because the second transaction must come after the the first transaction, we need to calculate it from the last element to the first one.
Or we can understand in this way:
total_maximum_profit = maxProfit1st[i] + max_profit_from_i_to_length-1 (maxProfit2nd);

Doing it reversely:

maxProfit2nd
6, 6, 6, 6, 6, 6, 4, 0

So the total_maximum_profit = max(maxProfit1st[i], maxProfit2nd[i]).



public class MaxProfit {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0)
            return 0;
        //First transaction from 0th price to ith price
        int[] maxProfit1st = new int[prices.length];
        int min = prices[0];
        maxProfit1st[0] = 0;
        for (int i = 1; i < maxProfit1st.length; i++)
        {
            min = Math.min(min, prices[i]);
            maxProfit1st[i] = Math.max(prices[i] - min, maxProfit1st[i - 1]);
        }
        //Second transaction from ith price to (length - 1)-th price
        int[] maxProfit2nd = new int[prices.length];
        maxProfit2nd[prices.length - 1] = 0;
        int max = prices[prices.length - 1];
        for (int i = maxProfit2nd.length - 2; i >= 0; i--)
        {
            max = Math.max(max, prices[i]);
            maxProfit2nd[i] = Math.max(max - prices[i], maxProfit2nd[i + 1]);
        }
        int maxProfit = 0;
        for (int i = 0; i < prices.length; i++)
        {
            maxProfit = Math.max(maxProfit, maxProfit1st[i] + maxProfit2nd[i]);
        }
        return maxProfit;
    }
}

Palindrome Partitioning I & II

Given a string s, partition s such that every substring of the partition is a palindrome.
Return all possible palindrome partitioning of s.
For example, given s = "aab",
Return
  [
    ["aa","b"],
    ["a","a","b"]
  ]
The first problem is a typical backtracking problem. Start from index 0. We check each substring, and recursively check the next substring, until we reach the end of the string. If we successfully add all substrings into the partition list, we can add this partition to the rst list.

For example: s = "aab"

start = 0
check substring(0, 1) -> (1, 2) -> (2, 3) -> add
remove (2, 3)
(0, 1) -> (1, 3) not palindrome
(0, 2) -> (2, 3) -> add



public class PalindromePartition {
    public ArrayList> partition(String s) {
        if (s == null)
            throw new NullPointerException("Null string!");
        ArrayList> rst = new ArrayList> ();
        if (s.length() == 0)
            return rst;
        partitionString(s, rst, new ArrayList (), 0);
        return rst;
       
    }
    private void partitionString (String s, ArrayList> rst, ArrayList partition, int start) {
        if (start == s.length()) {
            rst.add(new ArrayList (partition));
            return;
        }
        for (int i = start + 1; i <= s.length(); i++) {
            String tmp = s.substring(start, i);
            if (!isPalindrome(tmp))
                continue;
            partition.add(tmp);
            partitionString(s, rst, partition, i);
            partition.remove(partition.size() - 1);
        }
    }
    private boolean isPalindrome(String s) {
        int start = 0;
        int end = s.length() - 1;
        while (start < end) {
            if (s.charAt(start) != s.charAt(end))
                return false;
            start++;
            end--;
        }
        return true;
    }
    
}


Given a string s, partition s such that every substring of the partition is a palindrome.
Return the minimum cuts needed for a palindrome partitioning of s.
For example, given s = "aab",
Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut.

I am definitely not good at DP, especially when I need to it.. twice...

This problem is obviously a typical DP (when it comes to find the maximum or minimum, you probably want to start your approach of DP first).

I like to show things using examples, mainly because I am not at describing things.

s = "abbab"
        s.charAt(i)             0  1  2  3  4
        s                             a  b  b  a  b
possible places to cut: 0  1  2  3  4  5

So I define an array cut[s.length() + 1]. cut[i] is aimed to store the minimum cut needed for s.substring(0, i).
cut[0] =  0, obviously, empty string.
cut[1] = 0, because s.substring(0,1) is a palindrome (single character).
start from cut[2]:
    1. if s.substring(0, i) is a palindrome, cut[i] = 0;
    2. otherwise, if s.substring(start,i) (start = 1, ... i - 1) is a palindrome, cut[i] = cut[start] + 1;
return cut[s.length], which is cut[5] in this case.

The next part is, how to check if s.substring(start, end) is a palindrome. Of course we can do it iteratively, but is it more convenient if we draw a table of all palindrome substrings of s and just check the table every time?
Yep, that's pretty much the solution.

1. palindrome[i][i] is obviously a palindrome since it only contains one character.
2. palindrome[i][i + 1] is a palindrome if s.charAt(i) == s.charAt(j)
3. palindrome[i][j] is a palindrome if palindrome[i+1][j-1] && s.charAt(i) ==s.charAt(j)

start \ end   0   1   2   3    4
        0        T   F   F   T    F
        1             T   T   T    F
        2                  T    F   T
        3                        T   F
        4                             T




public class PalindromePartition {
    public int minCut(String s) {
        if (s == null || s.length() == 0)
            return 0;
        //**************************************
        // I write this single loop to check if the string itself is a plindrome, 
        //alternatively you can include it into the matrix or write a boolean checkPalindrome (s) method
        int startp = 0;
        int endp = s.length() - 1;
        while (startp < endp)
        {
         if(s.charAt(startp) != s.charAt(endp))
          break;
         startp++;
         endp--;
        }
        if (endp <= startp)
         return 0;
        //****************************************
        int[] cut = new int[s.length() + 1];
        boolean[][] palindromeMatrix = isPalindrome(s);
        cut[0] = 0;
        cut[1] = 0;
        for (int end = 2; end < cut.length; end++)
        {
            cut[end] = Integer.MAX_VALUE;
            if (palindromeMatrix[0][end - 1])
            {
                cut[end] = 0;
                continue;
            }
            for (int start = 1; start < end; start++)
            {
                if (!palindromeMatrix[start][end - 1])
                    continue;
                cut[end] = Math.min(cut[end], cut[start] + 1);
            }
        }
        return cut[s.length()];
    }
    private boolean[][] isPalindrome(String s)
    {
        boolean[][] palindromeMatrix = new boolean[s.length()][s.length()];
        for (int i = 0; i < s.length(); i++)
            palindromeMatrix[i][i] = true;
        for (int i = 0; i < s.length() - 1; i++)
            palindromeMatrix[i][i + 1] = (s.charAt(i) == s.charAt(i + 1));
        for (int length = 3; length < s.length(); length++)
        {
            for (int start = 0; start <= s.length() - length; start++)
                palindromeMatrix[start][start + length - 1] = (palindromeMatrix[start + 1][start + length - 2] && (s.charAt(start) == s.charAt(start + length - 1)));
        }
        return palindromeMatrix;
    }
}

Saturday, December 13, 2014

Unique Binary Search Tree I and II

A very interesting problem. At first glance, I had no idea how to approach it. Thanks to Stackoverflow (again), for helping me solve the problem.

According to Binary Search Tree's definition, in order for node i to be the root, it's left subtree can only be generated from node 0 to i - 1, and it's right subtree can only be generated from i + 1 to n.

So, Tree[n] = Tree[0] * Tree[n - 1] + Tree[1] * Tree[n - 2] + ... + Tree[i] * Tree[n - i - 1] + ... + Tree[n - 1] * Tree[0]


public class UniqueBST {
    public int numTrees(int n) {
        if (n < 0)
            return 0;
        if (n == 0 || n == 1)
            return 1;
        int[] Tree = new int[n + 1];
        Tree[0] = 1;
        Tree[1] = 1;
        
        for (int i = 2; i <= n; i++)
        {
            //j represents the left subtree
            for (int j = 0; j < i; j++)
                Tree[i] += Tree[j] * Tree[i - j - 1];
        }
        return Tree[n];
        
    }
}

The second problem is derived from the same idea. The only difference is, now we need to use recursion to generate every left and right subtree.



public class Solution {
    public ArrayList generateTrees(int n) {
        return generateTreeHelper(1, n);
    }
    private ArrayList generateTreeHelper(int start, int end)
    {
        ArrayList rst = new ArrayList();
        //reach to the leaf
        if (start > end)
        {
            rst.add(null);
            return rst;
        }
        for (int i = start; i <= end; i++)
        {
            ArrayList left = generateTreeHelper(start, i - 1);
            ArrayList right = generateTreeHelper(i + 1, end);
            for (TreeNode l : left)
            {
                for (TreeNode r : right)
                {
                    TreeNode root = new TreeNode(i);
                    root.left = l;
                    root.right = r;
                    rst.add(root);
                }
            }
            
        }
        return rst;
    }
}

Word Break

Typical DP, finally! My initial goal is reviewing DP today. But both of the previous two problems had a better solution than DP.

i: a potential break point;
j: the length of the substring need to be checked, j must be smaller than the length of the longest word in the dictionary
canbeBreak[i]: if the i-th position is a break
canbeBreak[i] = canbeBreak[i - j] && dict.contains(i - j, i): i is a break point if there exists a cut at i -  j that is also a break point, and s.substring(i - j, i) is in the dictionary. 


public class Solution {
    public boolean wordBreak(String s, Set dict) {
        if (s == null || dict.isEmpty())
            return false;
        int maxlength = getMaxLength(dict);
        
        //canbeBreak[i]: check if i-th position is a break
        boolean[] canbeBreak = new boolean[s.length() + 1];
        canbeBreak[0] = true;
        
        for (int i = 1; i <= s.length(); i++)
        {
            canbeBreak[i] = false;
            for (int j = 1; j <= i && j <= maxlength; j++)
            {
                //i - j, the break position, if the first "half" of the substring is not in the dictionary
                // continue
                if(!canbeBreak[i - j])
                    continue;
                String word = s.substring(i - j, i);
                if (dict.contains(word))
                {
                    canbeBreak[i] = true;
                    break;
                }
            }
            
        }
        return canbeBreak[s.length()];
        
    }
    //if the length of the substring is larger than the longest word in the dictionary, there is no need to check
    private int getMaxLength(Set dict)
    {
        int maxlength = 0;
        for (String word : dict)
            maxlength = Math.max(maxlength, word.length());
        return maxlength;
    }
}

Scramble String

A very interesting problem. I like this recursion solution, very neat. Remember to check every boundary cases in order to reduce recursions

Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.
Below is one possible representation of s1 = "great":
    great
   /    \
  gr    eat
 / \    /  \
g   r  e   at
           / \
          a   t
To scramble the string, we may choose any non-leaf node and swap its two children.
For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat".
    rgeat
   /    \
  rg    eat
 / \    /  \
r   g  e   at
           / \
          a   t
We say that "rgeat" is a scrambled string of "great".
Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae".
    rgtae
   /    \
  rg    tae
 / \    /  \
r   g  ta  e
       / \
      t   a
We say that "rgtae" is a scrambled string of "great".
Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.

Update a DP solution. Consider a simple case "gre". The string may be split to "g" and "re", or "gr" and "e". So a scrambled string of "gre" can be "ger", "rge", "reg", "egr", "erg" or itself ("gre").  The DP solution uses an 3D matrix, scramble[k][i][j], the first dimension indicates the length of the substring, and the second and third dimension indicate the start index of first and second string, respectively (s1.substring(i, i + k) and s2.substring(j, j + k)).   So similar to the recursion solution, we check every substring of s1 and s2 and construct the matrix.



public boolean isScramble(String s1, String s2) {
        if (s1.length() != s2.length())
            return false;
        if (s1.length() == 0 || s1.equals(s2))
            return true;
        
        int length = s1.length();
        boolean[][][] scramble = new boolean[length][length][length];
        
        for (int i = 0; i < length; i++) {
            for (int j = 0; j < length; j++) {
                scramble[0][i][j] = s1.charAt(i) == s2.charAt(j) ? true : false;
            }
        }
        
        for (int len = 2; len <= length; len++) { //the length of substring
            for (int i = 0; i <= length - len; i++) {
                for (int j = 0; j <= length - len; j++) {
                    boolean r = false;
                    for (int k = 1; k < len && r == false; k++) {
                        r = (scramble[k - 1][i][j] && scramble[len - k - 1][i + k][j + k])
                        || (scramble[k - 1][i][j + len - k] && scramble[len - k - 1][i + k][j]);
                    }
                    scramble[len - 1][i][j] = r;
                }
            }
        }
        return scramble[length - 1][0][0];
    }





public class ScrambleString {
    public boolean isScramble(String s1, String s2) {
        if ((s1 == null && s2 != null) || (s1 != null && s2 == null))
            return false;
        if (s1.length() != s2.length())
            return false;
        if (s1.equals(s2))
            return true;
        int length = s1.length();
        int chars1 = 0;
        int chars2 = 0;
        
        // check if two strings are consisted by same characters
        for (int i = 0; i < length; i++)
        {
            chars1 += Character.getNumericValue(s1.charAt(i));
            chars2 += Character.getNumericValue(s2.charAt(i));
        }
        if (chars1 != chars2)
            return false;
            
        if (s1.length() == 1)
            return true;
        //the string must be swapped at certain position assume s2 is a scramble string
        //iterate through all positions and recursively check 
        for (int i = 1; i < length; i++)
        {
            if (isScramble(s1.substring(0,i), s2.substring(0,i)) && isScramble(s1.substring(i),s2.substring(i)))
                return true;
            // if the string is reversed, the reversed one is also a scramble string
            if (isScramble(s1.substring(0,i), s2.substring(length - i)) && isScramble(s1.substring(i), s2.substring(0, length - i)))
                return true;
        }
        return false;
    }
}


Update: 2015 - 01 -18
I did a performance test on "great" and "rgtae" for both methods:

Yeah, recursion is preferred. :)