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.
Dynamic Programming String
memorization(翻译为记忆化搜索)。 这个版面没有任何剪纸和缓存,所以超时了 Time Limit Exceeded
class Solution { public: bool isScramble(string s1, string s2) { //cout << "s1\t" << s1 << endl; //cout << "s2\t" << s2 << endl; if(s1.size() != s2.size()) return false; if(s1 == s2) return true; int s1Size = s1.size(); int s2Size = s2.size(); int s1aSize; int s1bSize; for(s1aSize = 1;s1aSize < s1Size ; s1aSize++ ) { s1bSize = s1Size - s1aSize; //cout << "s1aSize\t" << s1aSize << endl; //cout << "s1bSize\t" << s1bSize << endl; if( (isScramble(s1.substr(0,s1aSize), s2.substr(0,s1aSize)) && isScramble(s1.substr(s1aSize, s1bSize), s2.substr(s1aSize, s1bSize))) || (isScramble(s1.substr(0,s1aSize), s2.substr(s1bSize,s1aSize)) && isScramble(s1.substr(s1aSize, s1bSize), s2.substr(0, s1bSize))) ) return true; } return false; } };
思路二,在递归的基础上进行剪枝,判断两个字符串是否互为 scamble,至少要求每个字符在两个字符串中出现的次数要相等,如果不相等则返回 false。
成功AC
class Solution { public: bool isScramble(string s1, string s2) { //cout << "s1\t" << s1 << endl; //cout << "s2\t" << s2 << endl; if(s1.size() != s2.size()) return false; if(s1 == s2) return true; int s1Size = s1.size(); int s2Size = s2.size(); int s1aSize; int s1bSize; int cnt[256]; fill(cnt, cnt+ 256, 0); for(int i = 0; i < s1Size; i++) { cnt[s1[i]] ++; } for(int i = 0; i < s2Size; i++) { cnt[s2[i]] --; } for(int i = 0; i < 256; i++) { if(cnt[i] != 0) return false; } for(s1aSize = 1;s1aSize < s1Size ; s1aSize++ ) { s1bSize = s1Size - s1aSize; //cout << "s1aSize\t" << s1aSize << endl; //cout << "s1bSize\t" << s1bSize << endl; if( (isScramble(s1.substr(0,s1aSize), s2.substr(0,s1aSize)) && isScramble(s1.substr(s1aSize, s1bSize), s2.substr(s1aSize, s1bSize))) || (isScramble(s1.substr(0,s1aSize), s2.substr(s1bSize,s1aSize)) && isScramble(s1.substr(s1aSize, s1bSize), s2.substr(0, s1bSize))) ) return true; } return false; } };
思路三:备忘录法,其实就是递归+ 缓存(cache),也成功AC
class Solution { map<string, bool> cache; public: bool isScramble(string s1, string s2) { cache.clear(); return isScrambleInternal(s1, s2); } bool isScrambleInternal(string s1, string s2) { //cout << "s1\t" << s1 << endl; //cout << "s2\t" << s2 << endl; if(s1.size() != s2.size()) return false; if(s1 == s2) return true; int s1Size = s1.size(); int s2Size = s2.size(); int s1aSize; int s1bSize; for(s1aSize = 1;s1aSize < s1Size ; s1aSize++ ) { s1bSize = s1Size - s1aSize; if( (getOrUpdate(s1.substr(0,s1aSize), s2.substr(0,s1aSize)) && getOrUpdate(s1.substr(s1aSize, s1bSize), s2.substr(s1aSize, s1bSize))) || (getOrUpdate(s1.substr(0,s1aSize), s2.substr(s1bSize,s1aSize)) && getOrUpdate(s1.substr(s1aSize, s1bSize), s2.substr(0, s1bSize))) ) { return true; } } return false; } bool getOrUpdate(string str1, string str2) { string str = str1 + str2; if(cache.count(str)) return cache[str]; bool res = isScrambleInternal(str1, str2); cache[str1+str2] = res; cache[str2+str1] = res; //cout << "str1\t" << str1<< endl; //cout << "str2\t" << str2<< endl; //cout << "res\t" << res<< endl; return res; } };
思路四:dp ,
既然可以用记忆化搜索,这题也一定可以用动规。设状态为 f[n][i][j],表示长度为 n,起
点为 s1[i] 和起点为 s2[j] 两个字符串是否互为 scramble,则状态转移方程为
f[n][i][j]} = (f[k][i][j] && f[n-k][i+k][j+k])
|| (f[k][i][j+n-k] && f[n-k][i+k][j])
class Solution { public: bool isScramble(string s1, string s2) { const int N = s1.size(); if (N != s2.size()) return false; // f[n][i][j],表示长度为 n,起点为 s1[i] 和 // 起点为 s2[j] 两个字符串是否互为 scramble bool f[N + 1][N][N];
//fill_n(f, (N+1)*N*N, false); // this is wrong fill_n(&f[0][0][0], (N + 1) * N * N, false); for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) f[1][i][j] = s1[i] == s2[j]; for (int n = 1; n <= N; ++n) { for (int i = 0; i + n <= N; ++i) { for (int j = 0; j + n <= N; ++j) { for (int k = 1; k < n; ++k) { if ((f[k][i][j] && f[n - k][i + k][j + k]) || (f[k][i][j + n - k] && f[n - k][i + k][j])) { f[n][i][j] = true; break; } } } } } return f[N][0][0]; } };