题目描述(困难难度)
把一个字符串按照二叉树的形状,分成两部分,注意,划分边界可以从任意节点开始,直到达到叶子节点。并且可以多次交换非叶子节点的两个子树,最后从左到右读取叶子节点,记为生成的字符串。题目是给两个字符串 S1 和 S2,然后问 S2 是否是 S1 经过上述方式生成的。
解法一 动态规划
解题思路
【分析】给定两个字符串 TT 和 SS,假设 TT 是由 SS 变换而来
- 如果 TT 和 SS 长度不一样,必定不能变来
- 如果长度一样,顶层字符串 S 能够划分为 S1和 S2,同样字符串 T 也能够划分为 T1和 T2.
- 情况一:没交换,S1 ==> T1,S2 ==> T2
- 情况二:交换了,S1 ==> T2,S2 ==> T1
- 子问题就是分别讨论两种情况,T1是否由S1变来,T2是否由 S_2变来,或 T1是否由S2变来,T2是否由 S_1变来.
class Solution {
public boolean isScramble(String s1, String s2) {
char[] chs1 = s1.toCharArray();
char[] chs2 = s2.toCharArray();
int n = s1.length();
int m = s2.length();
if (n != m) {
return false;
}
boolean[][][] dp = new boolean[n][n][n + 1];
//初始化单个字符的情况
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dp[i][j][1] = chs1[i] == chs2[j];
}
}
//枚举区间长度2~n
for (int len = 2; len <= n; len++) {
//枚举S中的起点位置
for (int i = 0; i <= n - len; i++) {
//枚举T中的起点位置
for (int j = 0; j <= n - len; j++) {
//枚举划分位置
for (int k = 1; k <= len - 1; k++) {
//第一种情况:S1->T1,S2->T2
if (dp[i][j][k] && dp[i + k][j + k][len - k]) {
dp[i][j][len] = true;
break;
}
//第二种情况:S1->T2,S2->T1
//S1起点i,T2起点j + 前面那段长度len-k,S2起点i+前面长度k
if (dp[i][j + len - k][k] && dp[i + k][j][len - k]) {
dp[i][j][len] = true;
break;
}
}
}
}
}
return dp[0][0][n];
}
}
时间复杂度: O(n^4)。
空间复杂度: O(n^3) 。
解法二 递归
思路和动态规划一样。
import java.util.Arrays;
public class Scramble_String {
public static boolean isScramble(String s1, String s2) {
if (s1.length() != s2.length()) return false;
if (s1.equals(s2)) return true; // 完全相同
//并不一定是完全相同,只要出现字母个数相同即可
int[] letters = new int[26];
for (int i = 0; i < s1.length(); i++) {
letters[s1.charAt(i) - 'a']++;
letters[s2.charAt(i) - 'a']--;
}
//如果两个字符串的字母出现不一致直接返回 false
for (int i = 0; i < 26; i++) {
if (letters[i] != 0) {
return false;
}
}
//遍历每个切割位置
for (int i = 1; i < s1.length(); i++) {
if (
// 第一种情况:s1_left=s2_left,s1_right=s2_right;
isScramble(s1.substring(0, i), s2.substring(0, i)) && isScramble(s1.substring(i,s1.length()), s2.substring(i,s2.length()))
||
// 第二种情况:s1_left=s2_right,s1_right=s2_left;
isScramble(s1.substring(0,i), s2.substring(s2.length() - i,s2.length())) &&isScramble(s1.substring(i,s1.length()), s2.substring(0,s2.length() - i))
) {
return true;
}
}
return false;
}
public static void main(String args[]) {
String s1="abcde";
String s2="caebd";
boolean ans=isScramble(s1,s2);
System.out.println(ans);
}
}
总结
这题在面试中很少被考到,其中的动态规划时间复杂度和空间复杂度太高,不建议在较高维度空间使用。动态规划一般在二维数组中更为常见。
参考文献
1.https://www.youtube.com/watch?v=Lq3Kr7-qXGI
2.https://zhuanlan.zhihu.com/p/69501503
3.https://leetcode-cn.com/problems/scramble-string/solution/miao-dong-de-qu-jian-xing-dpsi-lu-by-sha-yu-la-jia/