【leetcode刷题笔记】Scramble String

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.


 

题解:

第一个想法比较naive,想把s1和s2都按照字典序排列,然后看是否相同。提交后就发现下面的例子就能够证明这个算法的错误:

s1 = "abcd"
s2 = "bdac"

第二个想法是递归,如下图所示:

对于任意长度相等的s1和s2,都可以分解成上图两种情况,通过把s1和s2分成两部分,那么有两种情况s1和s2是scramble的,一种是两部分对应scramble;一种是两部分交换后scramble。这样就有了我们的递归解法。

但是仅仅是这种递归一定会超时,必须进行优化,有一下几点可以优化的地方:

  1. s1和s2长度必须相等;
  2. 长度相等且都为0,返回true;
  3. s1和s2完全相同返回true;
  4. s1和s2所包含的字母种类个数必须相同,即s1和s2按照字典序排序后必须相同;

通过以上的优化,递归方法就不会超时了。

代码如下:

 1 public class Solution {
 2     public boolean isScramble(String s1, String s2) {
 3         int m = s1.length();
 4         int n = s2.length();
 5         
 6         if(m != n)
 7             return false;
 8         
 9         if(m == 0 || s1.equals(s2))
10             return true;
11             
12         char[] chars1 = s1.toCharArray();
13         char[] chars2 = s2.toCharArray();
14         Arrays.sort(chars1);
15         Arrays.sort(chars2);
16         for(int i = 0;i < m;i++)
17             if(chars1[i] != chars2[i])
18                 return false;
19         
20         for(int i = 1;i < m;i++){
21             String s1left = s1.substring(0,i);
22             String s1right = s1.substring(i);
23             String s2left = s2.substring(0, i);
24             String s2right = s2.substring(i);
25             
26             if(isScramble(s1left, s2left) && isScramble(s1right, s2right))
27                 return true;
28             s2left = s2.substring(0,n-i);
29             s2right = s2.substring(n-i);
30             if(isScramble(s1left, s2right) && isScramble(s1right, s2left))
31                 return true;
32         }
33         
34         return false;
35     }
36 }

上述方法耗时428ms。

第三个想法,动态规划。

递归之所以会超时,是因为做了许多重复的工作。用动态规划bottom-up的方法把这些重复的工作记录在表dp[][][]中,就可以省去重复计算的时间。

我们用dp[sublen][i][j]表示s1[i,i+sublen]和s2[j,j+sublen]是否是scramble的。那么有如下递推式:

dp[sublen][i][j] = (dp[k][i][j]&&dp[sublen-k][i+k][j+k]) || (dp[k][i][j+sublen-k] && dp[sublen-k][i+k][j]);

它对应了下图两种情况:

另外 dp[1][i][j] = s1.charAt(i) == s2.charAt(j); 

理解还算容易,写代码还是有难度的,一共四重循环:

第一重循环改变sublen的大小,从长度为2的子串一直增加至长度为n的串;

第二重循环改变i,即s1中游标的位置,从0~n-sublen;

第三重循环改变i,即s2中游标的位置,从0~n-sublen;

第四重循环枚举从i~i+sublen之间的每一中分割字符串的位置,考察这样分割是否能使得s1和s2对应的部分scramble得到,k从1~sublen-1;

代码如下:

 1 public class Solution {
 2     public boolean isScramble(String s1, String s2) {
 3         if(s1.length() != s2.length())
 4             return false;
 5         
 6         if(s1.length() == 0 && s2.length() == 0)
 7             return true;
 8         
 9         int n = s1.length();
10         boolean[][][] dp = new boolean[n+1][n+1][n+1];
11         for(int i = 0;i < n;i++){
12             for(int j = 0;j < n;j++)
13                 dp[1][i][j] = s1.charAt(i) == s2.charAt(j); 
14         }
15         
16         for(int sublen = 2;sublen <= n;sublen++){ 
17             for(int i = 0;i <= n-sublen;i++){
18                 for(int j = 0;j <= n-sublen;j++){
19                     dp[sublen][i][j]= false; 
20                     for(int k = 1;k<sublen && dp[sublen][i][j]== false ;k++)
21                         dp[sublen][i][j] = (dp[k][i][j]&&dp[sublen-k][i+k][j+k]) || (dp[k][i][j+sublen-k] && dp[sublen-k][i+k][j]);  
22                 }
23             }
24         }
25         return dp[n][0][0];
26     }
27 }

转载于:https://www.cnblogs.com/sunshineatnoon/p/3862847.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值