Given a stringS1, We may represent it as a binary tree by partitioning it to two non-empty substrings recursively.
Below is one possible representationS1="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"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"great"
.
Given two stringsS1AndS2Of the same length, determine ifS2Is a scrambled stringS1.
Question:
The first idea is more like naive. I want to sort S1 and S2 in lexicographically order and check whether they are the same. After submission, we can find the following example to prove the error of this algorithm:
s1 = "abcd"s2 = "bdac"
The second idea is recursion, as shown in:
For any S1 and S2 with the same length, it can be divided into two cases. By dividing S1 and S2 into two parts, S1 and S2 are scramble in two cases, one is scramble, and the other is scramble after the two parts are switched. This gives us a recursive solution.
However, only such recursion will definitely time out and must be optimized. There are several points that can be optimized:
- S1 and S2 must be of the same length;
- Returns true if the length is equal and all values are 0;
- Returns true if S1 and S2 are identical;
- S1 and S2 must have the same number of letter types, that is, S1 and S2 must be sorted in Lexicographic Order;
Through the above optimization, the recursive method will not time out.
The Code is as follows:
1 public class Solution { 2 public boolean isScramble(String s1, String s2) { 3 int m = s1.length(); 4 int n = s2.length(); 5 6 if(m != n) 7 return false; 8 9 if(m == 0 || s1.equals(s2))10 return true;11 12 char[] chars1 = s1.toCharArray();13 char[] chars2 = s2.toCharArray();14 Arrays.sort(chars1);15 Arrays.sort(chars2);16 for(int i = 0;i < m;i++)17 if(chars1[i] != chars2[i])18 return false;19 20 for(int i = 1;i < m;i++){21 String s1left = s1.substring(0,i);22 String s1right = s1.substring(i);23 String s2left = s2.substring(0, i);24 String s2right = s2.substring(i);25 26 if(isScramble(s1left, s2left) && isScramble(s1right, s2right))27 return true;28 s2left = s2.substring(0,n-i);29 s2right = s2.substring(n-i);30 if(isScramble(s1left, s2right) && isScramble(s1right, s2left))31 return true;32 }33 34 return false;35 }36 }
The preceding method takes 428 Ms.
The third idea is dynamic planning.
Recursion times out because it does a lot of repetitive work. You can use the dynamic planning bottom-up method to record the repetitive work in the DP [] [] [] Table to save the time for repeated computation.
We use DP [sublen] [I] [J] to indicate whether S1 [I, I + sublen] and S2 [J, J + sublen] are scramble. The following recursive formula is available:
dp[sublen][i][j] = (dp[k][i][j]&&dp[sublen-k][i+k][j+k]) || (dp[k][i][j+sublen-k] && dp[sublen-k][i+k][j]);
It corresponds to two situations:
In addition, DP [1] [I] [J] = s1.charat (I) = s2.charat (j );
It is easy to understand, and it is still difficult to write code. There are four loops in total:
In the first loop, the size of sublen is changed from the substring with the length of 2 to the substring with the length of N;
The second cycle changes I, that is, the position of the target in S1, from 0 ~ N-sublen;
The third re-cycle changes I, that is, the position of the target in S2, from 0 ~ N-sublen;
The forth recyclically enumerated values are from I ~ Position of each split string between I + sublen to check whether the split can result in scramble of S1 and S2. K ranges from 1 ~ Sublen-1;
The Code is as follows:
1 public class Solution { 2 public boolean isScramble(String s1, String s2) { 3 if(s1.length() != s2.length()) 4 return false; 5 6 if(s1.length() == 0 && s2.length() == 0) 7 return true; 8 9 int n = s1.length();10 boolean[][][] dp = new boolean[n+1][n+1][n+1];11 for(int i = 0;i < n;i++){12 for(int j = 0;j < n;j++)13 dp[1][i][j] = s1.charAt(i) == s2.charAt(j); 14 }15 16 for(int sublen = 2;sublen <= n;sublen++){ 17 for(int i = 0;i <= n-sublen;i++){18 for(int j = 0;j <= n-sublen;j++){19 dp[sublen][i][j]= false; 20 for(int k = 1;k<sublen && dp[sublen][i][j]== false ;k++)21 dp[sublen][i][j] = (dp[k][i][j]&&dp[sublen-k][i+k][j+k]) || (dp[k][i][j+sublen-k] && dp[sublen-k][i+k][j]); 22 }23 }24 }25 return dp[n][0][0];26 }27 }