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. :)

why is the anagram test necessary??
ReplyDeleteshouldnot it be taken care of by the remaining code.
Found a really good explanation of this question
ReplyDeletehttps://youtu.be/uqRrb4t_ktk