[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.

 

Hide Tags
  Dynamic Programming String
 
 
思路一: 递归, 首先想到的是递归(即深搜),对两个 string 进行分割,然后比较四对字符串。代码虽然简单, 但是复杂度比较高。有两种加速策略,一种是剪枝,提前返回;一种是加缓存,缓存中间结果,即

memorization(翻译为记忆化搜索)。 这个版面没有任何剪纸和缓存,所以超时了 Time Limit Exceeded

class Solution {
    public:
        bool isScramble(string s1, string s2) {
            //cout << "s1\t" << s1 << endl;
            //cout << "s2\t" << s2 << endl;
            if(s1.size() != s2.size())
                return false;
            if(s1 == s2)
                return true;
            int s1Size = s1.size();
            int s2Size = s2.size();
            int s1aSize;
            int s1bSize;

            for(s1aSize = 1;s1aSize < s1Size ; s1aSize++ )
            {
                s1bSize =  s1Size - s1aSize;
                //cout << "s1aSize\t" << s1aSize << endl;
                //cout << "s1bSize\t" << s1bSize << endl;

                if( (isScramble(s1.substr(0,s1aSize), s2.substr(0,s1aSize)) &&
                     isScramble(s1.substr(s1aSize, s1bSize), s2.substr(s1aSize, s1bSize)))
                     || (isScramble(s1.substr(0,s1aSize), s2.substr(s1bSize,s1aSize)) &&
                         isScramble(s1.substr(s1aSize, s1bSize), s2.substr(0, s1bSize))) )
                        return true;
            }
            return false;
        }
};

 

 思路二,在递归的基础上进行剪枝,判断两个字符串是否互为 scamble,至少要求每个字符在两个字符串中出现的次数要相等,如果不相等则返回 false。

成功AC

class Solution { 
    public: 
        bool isScramble(string s1, string s2) { 
            //cout << "s1\t" << s1 << endl; 
            //cout << "s2\t" << s2 << endl; 
            if(s1.size() != s2.size()) 
                return false; 
            if(s1 == s2) 
                return true; 
            int s1Size = s1.size(); 
            int s2Size = s2.size(); 
            int s1aSize; 
            int s1bSize; 
 
                  
            int cnt[256]; 
            fill(cnt, cnt+ 256, 0); 
 
            for(int i = 0; i < s1Size; i++)
            {
                cnt[s1[i]]  ++;
            }
            for(int i = 0; i < s2Size; i++)
            {
                cnt[s2[i]] --;
            }
            for(int i = 0; i < 256; i++)
            {
               if(cnt[i] != 0) 
                    return false;
            }


            for(s1aSize = 1;s1aSize < s1Size ; s1aSize++ )
            {
                s1bSize =  s1Size - s1aSize;
                //cout << "s1aSize\t" << s1aSize << endl;
                //cout << "s1bSize\t" << s1bSize << endl;

                if( (isScramble(s1.substr(0,s1aSize), s2.substr(0,s1aSize)) &&
                     isScramble(s1.substr(s1aSize, s1bSize), s2.substr(s1aSize, s1bSize))) 
                     || (isScramble(s1.substr(0,s1aSize), s2.substr(s1bSize,s1aSize)) &&
                         isScramble(s1.substr(s1aSize, s1bSize), s2.substr(0, s1bSize))) )
                        return true;
            }
            return false;
        }
};

 

 思路三:备忘录法,其实就是递归+ 缓存(cache),也成功AC

class Solution {
        map<string, bool>  cache;
    public:
        bool isScramble(string s1, string s2) {
            cache.clear();
            return isScrambleInternal(s1, s2);
        }

        bool isScrambleInternal(string s1, string s2) {
            //cout << "s1\t" << s1 << endl;
            //cout << "s2\t" << s2 << endl;
            if(s1.size() != s2.size())
                return false;
            if(s1 == s2)
                return true;
            int s1Size = s1.size();
            int s2Size = s2.size();
            int s1aSize;
            int s1bSize;

            for(s1aSize = 1;s1aSize < s1Size ; s1aSize++ )
            {
                s1bSize =  s1Size - s1aSize;

                if( (getOrUpdate(s1.substr(0,s1aSize), s2.substr(0,s1aSize)) &&
                            getOrUpdate(s1.substr(s1aSize, s1bSize), s2.substr(s1aSize, s1bSize))) 
                        || (getOrUpdate(s1.substr(0,s1aSize), s2.substr(s1bSize,s1aSize)) &&
                            getOrUpdate(s1.substr(s1aSize, s1bSize), s2.substr(0, s1bSize))) )
                {   
                    return true;
                }   
            }   
            return false;
        }   

        bool getOrUpdate(string str1, string str2)
        {
            string str = str1 + str2;
            if(cache.count(str))
                return cache[str];

            bool res = isScrambleInternal(str1, str2);
            cache[str1+str2] = res;
            cache[str2+str1] = res;
            //cout << "str1\t" << str1<< endl;
            //cout << "str2\t" << str2<< endl;
            //cout << "res\t" << res<< endl;
            return res;
        }
};

 思路四:dp ,

既然可以用记忆化搜索,这题也一定可以用动规。设状态为 f[n][i][j],表示长度为 n,起
点为 s1[i] 和起点为 s2[j] 两个字符串是否互为 scramble,则状态转移方程为
f[n][i][j]} = (f[k][i][j] && f[n-k][i+k][j+k])
|| (f[k][i][j+n-k] && f[n-k][i+k][j])

 

class Solution {
    public:
        bool isScramble(string s1, string s2) {
            const int N = s1.size();
            if (N != s2.size()) return false;
        // f[n][i][j],表示长度为 n,起点为 s1[i] 和
        // 起点为 s2[j] 两个字符串是否互为 scramble
            bool f[N + 1][N][N];
      //fill_n(f, (N+1)*N*N, false); // this is wrong fill_n(
&f[0][0][0], (N + 1) * N * N, false); for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) f[1][i][j] = s1[i] == s2[j]; for (int n = 1; n <= N; ++n) { for (int i = 0; i + n <= N; ++i) { for (int j = 0; j + n <= N; ++j) { for (int k = 1; k < n; ++k) { if ((f[k][i][j] && f[n - k][i + k][j + k]) || (f[k][i][j + n - k] && f[n - k][i + k][j])) { f[n][i][j] = true; break; } } } } } return f[N][0][0]; } };

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值