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.

 

class Solution {
public:
    bool isScramble(string s1, string s2) {
		if(s1.empty() || s2.empty() || s1.size()!=s2.size()) return false;
        if(s1 == s2) return true;
        string c1(s1);
		sort(c1.begin(),c1.end());
        string c2(s2);
        sort(c2.begin(),c2.end());

        if(c1 != c2) return false;
        for(int i = 1;i < s1.size(); i++)
        {
            if(isScramble(s1.substr(0,i), s2.substr(0,i)) && isScramble(s1.substr(i), s2.substr(i))) 
				return true;
            if(isScramble(s1.substr(0,i), s2.substr(s2.length()-i)) && isScramble(s1.substr(i), s2.substr(0, s2.length()-i))) 
				return true;
        }
        return false;        
    }
};

 原题链接: http://oj.leetcode.com/problems/scramble-string/ 
这道题看起来是比较复杂的,如果用brute force,每次做切割,然后递归求解,是一个非多项式的复杂度,一般来说这不是面试官想要的答案。
这其实是一道三维动态规划的题目,我们提出维护量res[i][j][n],其中i是s1的起始字符,j是s2的起始字符,而n是当前的字符串长度,res[i][j][len]表示的是以i和j分别为s1和s2起点的长度为len的字符串是不是互为scramble。
有 了维护量我们接下来看看递推式,也就是怎么根据历史信息来得到res[i][j][len]。判断这个是不是满足,其实我们首先是把当前 s1[i...i+len-1]字符串劈一刀分成两部分,然后分两种情况:第一种是左边和s2[j...j+len-1]左边部分是不是 scramble,以及右边和s2[j...j+len-1]右边部分是不是scramble;第二种情况是左边和s2[j...j+len-1]右边部 分是不是scramble,以及右边和s2[j...j+len-1]左边部分是不是scramble。如果以上两种情况有一种成立,说明 s1[i...i+len-1]和s2[j...j+len-1]是scramble的。而对于判断这些左右部分是不是scramble我们是有历史信息 的,因为长度小于n的所有情况我们都在前面求解过了(也就是长度是最外层循环)。
上面说的是劈一刀的情况,对于s1[i...i+len-1]我们有len-1种劈法,在这些劈法中只要有一种成立,那么两个串就是scramble的。
总 结起来递推式是res[i][j][len] = || (res[i][j][k]&&res[i+k][j+k][len-k] || res[i][j+len-k][k]&&res[i+k][j][len-k]) 对于所有1<=k<len,也就是对于所有len-1种劈法的结果求或运算。因为信息都是计算过的,对于每种劈法只需要常量操作即可完成,因 此求解递推式是需要O(len)(因为len-1种劈法)。
如此总时间复杂度因为是三维动态规划,需要三层循环,加上每一步需要线行时间求解递推式,所以是O(n^4)。虽然已经比较高了,但是至少不是指数量级的,动态规划还是有很大有事的,空间复杂度是O(n^3)。代码如下:

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

个 人觉得这是LeetCode中最难的动态规划的题目了,要进行一次三维动态规划,对于维护量的含义也比较讲究。有朋友会讨论这个维护量是怎么提出来的,我 自己也没什么绝对的方法,还是熟能生巧,靠“感觉”,做的题目多了就自然来了,这个做高中数学题有点类似哈,辅助线是靠“灵感”的哈。面试中如果遇到就是 top难度的了,不过即使如此,只要思路清晰,还是可以记住的。如果没做过,个人觉得比较难当场想出来,不过算法大牛就另说了,这种题很经常出现在编程比 赛中,ACM高手还是不在话下的哈。

 

 

class Solution {
public:
    bool isScramble(string s1, string s2) {
    	if(s1.empty() || s2.empty() || s1.size()!=s2.size())  
            return false;  
        if(s1.size()==0)  
            return true;  
    	const int n = s1.size();
    	vector<vector<vector<bool> > > res(n,vector<vector<bool> >(n,vector<bool>(n+1)));
    	
        for(int i = 0;i<n;i++)  
        {  
            for(int j=0;j<n;j++)  
            {  
                res[i][j][1] = s1[i]==s2[j];  
            }  
        }  
        for(int len=2;len<=n;len++)  
        {  
            for(int i=0;i<n-len+1;i++)  
            {  
                for(int j=0;j<n-len+1;j++)  
                {  
                    for(int k=1;k<len;k++)  
                    {  
                        res[i][j][len] = res[i][j][len] || res[i][j][k]&&res[i+k][j+k][len-k] || res[i][j+len-k][k]&&res[i+k][j][len-k];  
                    }  
                }  
            }  
        }  
        return res[0][0][s1.size()];     
    }
};

 

 

class Solution {
public:
    bool isScramble(string s1, string s2) {
        const int n = s1.size();
        if (n != s2.size()) return false;
        if (n == 0) return true;
        
        bool SC[n][n][n];
        for (int i = 0; i < n; i++) {  // len is 1
            for (int j = 0; j < n; j++) {
                SC[0][i][j] = s1[i] == s2[j];
            }
        }
        
        for (int len = 2; len <= n; len++) {
            for (int i = 0; i + len <= n; i++) {
                for (int j = 0; j + len <= n; j++) {
                    SC[len-1][i][j] = false;
                    for (int l = 1; l < len && !SC[len-1][i][j]; l++) {
                        SC[len-1][i][j] = ((SC[l-1][i][j] && SC[len-l-1][i+l][j+l]) || 
                            (SC[l-1][i][j+len-l] && SC[len-l-1][i+l][j]));
                    }
                }
            }
        }
        
        return SC[n-1][0][0];      
    }
};

 

posted on 2015-01-08 10:49  风云逸  阅读(90)  评论(0)    收藏  举报