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.

参考博文:http://www.cnblogs.com/easonliu/p/3696135.html

这道题与其说是DP更不如说是一道大递归。

因为题设给了一个二叉树的结构,而这个结构是递归定义的,更让人容易想起递归解法。

对于递归思路,自然是应当从小数据开始,例如长度为0、1、2等等。其实动态规划也是一样的,一个大问题分解为子问题才能找到问题的本质。

递归的思路如下:

(1)长度为1的时候,return s1==s2

(2)长度>1的时候,首先需要判断两个字符串包含的字符集是否相同,参考博文给了一种非常给力的剪枝即是如此。利用srot函数将整个字符串进行排序并比较,如果不相等则返回false。这是一个巨大的剪枝,也正因如此这样的递归才可以通过。

(3)长度>1的时候,将串分解成2个子串。s1、s2分别分解为a1a2和b1b2,需要判断a1==b1&&a2==b2 或者 a1==b2 && a2==b1。这里有非常重要的一点是在求子字符串的时候,需要注意判断之前要控制长度相等,否则会一直re的。

class Solution {
public:
    bool isScramble(string s1, string s2) {
        int m=s1.size();
        int n=s2.size();
        if(m!=n) return false;
        if(m==0) return true;
        if(m==1) return s1==s2;
        
        //要求字符集相同
        string temp1=s1,temp2=s2;
        sort(temp1.begin(),temp1.end());
        sort(temp2.begin(),temp2.end());
        if(temp1!=temp2) return false;
        
        bool result=false;
        for(int i=1;i<m && !result;i++){
            //以前i个字符进行分割
            string t11=s1.substr(0,i);
            string t12=s1.substr(i);
            string t21=s2.substr(0,i);
            string t22=s2.substr(i);
            result=isScramble(t11,t21) && isScramble(t12,t22);
            if(!result){
                //以后i个字符进行分割
                string t21=s2.substr(0,m-i);
                string t22=s2.substr(m-i);
                result=isScramble(t11,t22) && isScramble(t12,t21);
            }
        }
        return result;
    }
};

至于动归的解法,网上给出的是一个3维DP。

这里可以讨论一下DP的维度如何确定

一般而言,我们需要观察问题中有多少个独立变化的变量,那么DP的维度就是几维。

例如之前的乌龟棋问题,有4张牌+位置,正常思路肯定是5维,但是由于走过的路程严格等于4张牌对应维度的值进行运算得到,故该问题可以缩减为4维DP。

这个问题中,s1和s2下标本身肯定是独立变化的毋庸置疑,设置为i,j

最后一维比较难想也比较关键,利用k表示子字符串的长度,表示s1[i..i+k] s2[j..j+k]二者可以相互变化。

具体代码先研究一下吧…毕竟这题递归这么明显比较难想到

点赞