▶ 将一个单词 W1 以字母为最小单位拆成一棵二叉树,旋转其中的某几个结点的左右子树,再拼装回去得到单词 W2,我们称 W1 与 W2 等价。现在给出两个单词,判定他们是否等价。
● 代码,4 ms,递归,最快的解法算法与之相同。有方法利用 unordered_map 缓存了各子串的判定结果,但由于子串情况数较多,反而减速为 75 ms
1 class Solution 2 { 3 public: 4 bool isScramble(string s1, string s2) 5 { 6 if (s1 == s2)// 先做一些简单的比较,诸如字符数、每种字符的个数 7 return true; 8 if (s1.size() != s2.size()) 9 return false; 10 int i, len, count[26] = { 0 }; 11 for (i = 0, len = s1.size(); i < len; i++) 12 { 13 count[s1[i] - 'a']++; 14 count[s2[i] - 'a']--; 15 } 16 for (i = 0; i < 26; i++) 17 { 18 if (count[i]) 19 return false; 20 } 21 for (i = 1; i <= len - 1; i++) 22 { 23 if (isScramble(s1.substr(0, i), s2.substr(0, i)) && isScramble(s1.substr(i), s2.substr(i)))// 前半段跟前半段比,后半段跟后半段比 24 return true; 25 if (isScramble(s1.substr(0, i), s2.substr(len - i)) && isScramble(s1.substr(i), s2.substr(0, len - i)))// 前半段跟后半段比,后半段跟前半段比 26 return true; 27 } 28 return false; 29 } 30 };
● 代码,41 ms,三维动态规划,时间复杂度 O(n4)
1 class Solution 2 { 3 public: 4 bool isScramble(string s1, string s2) 5 { 6 const int sSize = s1.size(); 7 int len, i, j, k; 8 if (sSize == 0) 9 return true; 10 if (sSize == 1) 11 return s1 == s2; 12 vector<vector<vector<bool>>> isS(sSize + 1, vector<vector<bool>>(sSize, vector<bool>(sSize, false))); 13 // sS[i][j][k] 表示的是从 s1[j] 和 s2[k] 开始的长为 i 的子串,是否等价 14 for (i = 0; i < sSize; i++) 15 { 16 for (j = 0; j < sSize; j++) 17 isS[1][i][j] = s1[i] == s2[j]; 18 } 19 for (len = 2; len <= sSize; len++)// 每次循环填充一层 20 { 21 for (i = 0; i <= sSize - len; i++) 22 { 23 for (j = 0; j <= sSize - len; j++) 24 { 25 for (k = 1; k < len && !isS[len][i][j]; k++) 26 { 27 isS[len][i][j] = isS[len][i][j] || (isS[k][i][j] && isS[len - k][i + k][j + k]); 28 isS[len][i][j] = isS[len][i][j] || (isS[k][i + len - k][j] && isS[len - k][i][j + k]); 29 } 30 } 31 } 32 } 33 return isS[sSize][0][0]; 34 } 35 };
● 动态规划的另一个版本,31 ms
1 class Solution 2 { 3 public: 4 bool isScramble(string s1, string s2) 5 { 6 if (s1.size() != s2.size()) 7 return false; 8 const int len = s1.length(); 9 vector<vector<vector<bool>>> F(len, vector<vector<bool>>(len, vector<bool>(len + 1, false))); 10 // F(i, j, k) 表示从s1[i] 和 s2[j] 开始的长为 k 的子串是否等价 11 // 使用长度 q 来切割上述子串(q < k),分前半段、后半段分别对应的两种情况进行讨论 12 // s1 [ x1 | x2 ] 13 // i i + q i + k - 1 14 // s2 [ y1 | y2 ] 15 // j j + q j + k - 1 16 // s2 [ y1 | y2 ] 17 // j j + k - q j + k - 1 18 // 对于 k = 1,仅需检验 s1[i] 和 s2[j] 19 // 对于 k > 1 和 1 <= q < k,需要讨论 F(i, j, k) = (F(i, j, q) && F(i + q, j + q, k - q)) || (F(i, j + k - q, q) && F(i + q, j, k - q)); 20 for (int k = 1; k <= len; ++k) 21 { 22 for (int i = 0; i + k <= len; ++i) 23 { 24 for (int j = 0; j + k <= len; ++j) 25 { 26 if (k == 1) 27 F[i][j][k] = s1[i] == s2[j]; 28 else 29 { 30 for (int q = 1; q < k && !F[i][j][k]; ++q) 31 { 32 F[i][j][k] = (F[i][j][q] && F[i + q][j + q][k - q]) || (F[i][j + k - q][q] && F[i + q][j][k - q]); 33 } 34 } 35 } 36 } 37 } 38 return F[0][0][len]; 39 } 40 };