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.
思路:简单的说,就是s1和s2是scramble的话,那么必然存在一个在s1上的长度l1,将s1分成s11和s12两段,同样有s21和s22。
那么要么s11和s21是scramble的并且s12和s22是scramble的;
要么s11和s22是scramble的并且s12和s21是scramble的。
如果要能通过OJ,还必须把字符串排序后进行剪枝,也就是变成char array sort一下后,两者应该相等,否则没必要继续进行判断,这样可以剪掉很多例子;
public class Solution {
/**
* @param s1: A string
* @param s2: Another string
* @return: whether s2 is a scrambled string of s1
*/
public boolean isScramble(String s1, String s2) {
if(s1 == null && s2 == null) return true;
if(s1 == null || s2 == null || s1.length() != s2.length()) return false;
if(s1.length() ==1){
return s1.charAt(0) == s2.charAt(0);
}
if(!isValid(s1, s2)){
return false;
}
for(int i = 0; i < s1.length(); i++) {
String s11 = s1.substring(0,i);
String s12 = s1.substring(i);
String s21 = s2.substring(0,i);
String s22 = s2.substring(i);
String s23 = s2.substring(0, s2.length() - i);
String s24 = s2.substring(s2.length() - i);
if(isScramble(s11, s21) && isScramble(s12, s22)){
return true;
}
if(isScramble(s11, s24) && isScramble(s12, s23)){
return true;
}
}
return false;
}
private boolean isValid(String s1, String s2) {
char[] char1 = s1.toCharArray();
char[] char2 = s2.toCharArray();
Arrays.sort(char1);
Arrays.sort(char2);
String ns1 = new String(char1);
String ns2 = new String(char2);
if(!ns1.equals(ns2)){
return false;
}
return true;
}
}
DFS + memeory cache: 注意index表示的是在原来string里面的index,因为cache [i][j][k] 表达的意思是以s1 i 开始, s2 j 开始,长度为看的string是否是scramble;
public class Solution {
/**
* @param s1: A string
* @param s2: Another string
* @return: whether s2 is a scrambled string of s1
*/
public boolean isScramble(String s1, String s2) {
if(s1 == null && s2 == null) return true;
if(s1 == null || s2 == null || s1.length() != s2.length()) return false;
int n = s1.length();
int[][][] cache = new int[n][n][n+1];
return scrambleHelper(s1, 0, s2, 0, n, cache);
}
private boolean scrambleHelper(String s1, int start1, String s2, int start2, int k, int[][][] cache) {
if(cache[start1][start2][k] == 1) {
return true;
}
if(cache[start1][start2][k] == -1) {
return false;
}
if(s1.length() != s2.length()){
cache[start1][start2][k] = -1;
return false;
}
if(s1.length() == 1){
if(s1.charAt(0) == s2.charAt(0)){
cache[start1][start2][k] = 1;
return true;
} else {
cache[start1][start2][k] = -1;
return false;
}
}
for(int i = 1; i<s1.length(); i++) {
String s11 = s1.substring(0,i);
String s12 = s1.substring(i);
String s21 = s2.substring(0,i);
String s22 = s2.substring(i);
String s23 = s2.substring(0,s1.length() - i);
String s24 = s2.substring(s1.length() - i);
if(scrambleHelper(s11, start1, s21, start2, i, cache)
&& scrambleHelper(s12, start1+i, s22, start2 + i, k - i, cache)){
cache[start1][start2][k] = 1;
return true;
}
if(scrambleHelper(s11, start1, s24, start2+k-i, i, cache)
&& scrambleHelper(s12, start1+i, s23, start2, k-i, cache)){
cache[start1][start2][k] = 1;
return true;
}
}
cache[start1][start2][k] = -1;
return false;
}
private boolean isValid(String s1, String s2) {
char[] char1 = s1.toCharArray();
char[] char2 = s2.toCharArray();
Arrays.sort(char1);
Arrays.sort(char2);
String ns1 = new String(char1);
String ns2 = new String(char2);
if(!ns1.equals(ns2)){
return false;
}
return true;
}
}
DP version:
dp[i][j][k] 代表了s1从i开始,s2从j开始,长度为k的两个substring是否为scramble
string。
有三种情况需要考虑:
1. 如果两个substring相等的话,则为true
2. 如果两个substring中间某一个点,左边的substrings为scramble string,
同时右边的substrings也为scramble string,则为true
3. 如果两个substring中间某一个点,s1左边的substring和s2右边的substring为scramble
string, 同时s1右边substring和s2左边的substring也为scramble
string,则为true
注意: k<=n-Math.max(i,j)
boolean[][][] dp = new boolean [n][n][n+1]; 是N+1,因为dp[i][j][k] 代表的是s1从i开始,s2从j开始,长度为k的两个substring是否为scramble。
我们要判断dp[0][0][n] 所以,是n+1;
public class Solution {
public boolean isScramble(String s1, String s2) {
if(s1 == null || s2 == null) return false;
if(s1.length() != s2.length()) return false;
if(s1.length()==1 && s2.length()==1){
return s1.charAt(0) == s2.charAt(0);
}
int n = s1.length();
boolean[][][] dp = new boolean[n][n][n+1];
for(int i=n-1; i>=0; i--){
for(int j=n-1; j>=0; j--){
for(int k=1; k<=n-Math.max(i,j);k++){
if(s1.substring(i,i+k).equals(s2.substring(j,j+k))){
dp[i][j][k]=true;
}else{
for(int l=1; l<k; l++){
if( (dp[i][j][l] && dp[i+l][j+l][k-l]) || (dp[i][j+k-l][l] && dp[i+l][j][k-l]) ){
dp[i][j][k]=true;
break;
}
}
}
}
}
}
return dp[0][0][n];
}
}