题目链接: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.

这道题的要求是判断两个字符串是否是Scramble String。

如果两个字符串是Scramble String,则其长度必然相等。而且,在某位置切开s1,则s1左侧与s2左侧相同数量的子串为Scramble String,并且s1右侧与s2右侧相同数量的子串为Scramble String;或者s1左侧与s2右侧相同数量的子串为Scramble String,并且s1右侧与s2左侧相同数量的子串为Scramble String。

  1. 递归处理

思路有了,就可以通过递归调用遍历每组分割点了。其中a1和b1分别表示s1中以a1开始b1结束的子串,a2和b2分别表示s2中以a2开始b2结束的子串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
bool isScramble(string s1, int a1, int b1, string s2, int a2, int b2)
{
    if(b1 - a1 != b2 - a2)
        return false;
    if(a1 == b1)
        return s1[a1] == s2[a2];
    
    for(int i = a1; i < b1; ++ i)
    {
        if(isScramble(s1, a1, i, s2, a2, a2 + i - a1) && 
           isScramble(s1, i + 1, b1, s2, b2 - (b1 - i - 1), b2))
            return true;
        if(isScramble(s1, a1, i, s2, b2 - (i - a1), b2) && 
           isScramble(s1, i + 1, b1, s2, a2, a2 + b1 - i - 1))
            return true;
    }
    
    return false;
}

遗憾的是,超时。。。仔细想一下,还可以进行剪枝:如果s1和s2的子串中的字符不相同,则其必然不是Scramble String。

顺利AC。

时间复杂度:O(???)

空间复杂度:O(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution
{
public:
    bool isScramble(string s1, string s2)
    {
        return isScramble(s1, 0, s1.size() - 1, s2, 0, s2.size() - 1);
    }
private:
    bool isScramble(string s1, int a1, int b1, string s2, int a2, int b2)
    {
        if(b1 - a1 != b2 - a2)
            return false;
        if(a1 == b1)
            return s1[a1] == s2[a2];
        
        // 剪枝,如果子串中的字符不同,则必然不是Scramble String,不用再进行分割处理了
        int cnt[256] = {0};
        for(int i = a1; i <= b1; ++ i)
            ++ cnt[s1[i]];
        for(int i = a2; i <= b2; ++ i)
            -- cnt[s2[i]];
        for(int i = 0; i < 256 ;++ i)
            if(cnt[i] != 0)
                return false;
        
        for(int i = a1; i < b1; ++ i)
        {
            if(isScramble(s1, a1, i, s2, a2, a2 + i - a1) && 
               isScramble(s1, i + 1, b1, s2, b2 - (b1 - i - 1), b2))
                return true;
            if(isScramble(s1, a1, i, s2, b2 - (i - a1), b2) && 
               isScramble(s1, i + 1, b1, s2, a2, a2 + b1 - i - 1))
                return true;
        }
        
        return false;
    }
};
  1. 动态规划

维护变量dp[i][j][len],其中i是s1的起始字符,j是s2的起始字符,而n是当前的字符串长度,表示的是以i和j分别为s1和s2起点的长度为len的字符串是不是互为Scramble String。

递推公式也是前面的思路:dp[i][j][len]   = (dp[i][j][k]&&dp[i+k][j+k][len-k]   dp[i][j+len-k][k]&&dp[i+k][j][len-k])。

参考自这里

时间复杂度:O(???)

空间复杂度:O(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution
{
public:
    bool isScramble(string s1, string s2)
    {
        if(s1.size() == 0)
            return true;
        // dp[i][j][len]表示的是以i和j分别为s1和s2起点的长度为len的字符串是不是互为Scramble String
        vector<vector<vector<bool> > > dp(s1.size(), 
            vector<vector<bool> >(s2.size(), 
            vector<bool>(s1.size() + 1, false)));
        for(int i = 0; i < s1.size(); ++ i)
            for(int j = 0; j < s2.size(); ++ j)
                dp[i][j][1] = s1[i] == s2[j];
        
        for(int len = 2; len <= s1.size(); ++ len)
            for(int i = 0; i < s1.size() - len + 1; ++ i)
                for(int j = 0; j < s2.size() - len + 1; ++ j)
                    for(int k = 1; k < len; ++ k)
                        dp[i][j][len] = dp[i][j][len] || 
                                        dp[i][j][k] && dp[i + k][j + k][len - k] || 
                                        dp[i][j + len - k][k] && dp[i + k][j][len - k];
        
        return dp[0][0][s1.size()];
    }
};