leetcode-87. Scramble String

题目描述(困难难度)

在这里插入图片描述
把一个字符串按照二叉树的形状,分成两部分,注意,划分边界可以从任意节点开始,直到达到叶子节点。并且可以多次交换非叶子节点的两个子树,最后从左到右读取叶子节点,记为生成的字符串。题目是给两个字符串 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/

发布了168 篇原创文章 · 获赞 49 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/weixin_35770067/article/details/104792336