leetcode[86] Scramble String

將一個單詞按照這種方式分:html

Below is one possible representation of s1 = "great":node

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

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".idea

    rgtae
   /    \
  rg    tae
 / \    /  \
r   g  ta  e
       / \
      t   a

We say that "rgtae" is a scrambled string of "great".spa

這樣就說他們是scrambled string。如今給你兩個字符串怎麼判斷是否屬於scrambled string呢.net

思路:code

想了n久,木有什麼好的idea。htm

而後學習了justdoit兄的博客,理解了後本身也跟着寫了個遞歸的。

簡單的說,就是s1和s2是scramble的話,那麼必然存在一個在s1上的長度l1,將s1分紅s11和s12兩段,一樣有s21和s22。
那麼要麼s11和s21是scramble的而且s12和s22是scramble的;
要麼s11和s22是scramble的而且s12和s21是scramble的。

判斷剪枝是必要的,不然過不了大數據集合。

class Solution {
public:
bool subScramble(string s1, string s2)
{
    if (s1 == s2) return true;
    int len = s1.size();
    string sort_s1 = s1, sort_s2 = s2;
    sort(sort_s1.begin(), sort_s1.end());
    sort(sort_s2.begin(), sort_s2.end());
    if (sort_s1 != sort_s2) return false; //若是字母不相等直接返回錯誤
    for (int i = 1; i < len; ++i)
    {
        string s1_left = s1.substr(0,i);
        string s1_right = s1.substr(i);
        string s2_left = s2.substr(0,i);
        string s2_right = s2.substr(i);

        if (subScramble(s1_left, s2_left) && subScramble(s1_right, s2_right))
            return true;

        s2_left = s2.substr(0, len - i);
        s2_right = s2.substr(len - i);

        if (subScramble(s1_left, s2_right) && subScramble(s1_right, s2_left))
            return true;
    }
    return false;
}
bool isScramble(string s1, string s2)
{
    return subScramble(s1, s2);
}
};

 若是有更好的方法,咱們通常是不用遞歸的,由於遞歸每每複雜以至難以掌控。繼續學習,發現果真有動態規劃的方法。憑空想,確實不可思議。但看見以後又恍然明瞭。

本題遞歸複雜度是非多項式的。具體多少您探討一下。動態規劃的複雜度應該是O(n^4),連動態規劃都這個複雜度了,可見此題之可怕啊。

三維動態規劃:

dp[k][i][j]: s1的第i個開始長度爲k的串和s2的第j個開始長度爲k的串是否scrambled,是的話true,不然存false;

同遞歸核心思想,dp[k][i][j]應該能夠從1到k-1長度的分割來求解,一旦有一個爲true,那麼dp[k][i][j]就爲true並跳出。

即:

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

div從1到k-1,分兩種狀況,即s1的從i開始的div個和s2從j開始的div個scrambled而且從i+div開始的k-div個都匹配那麼true,

同理,若是s1的i開始的div個和s2的後div個匹配以及剩下另外兩部分也都匹配,那麼true。直到找到true位置。以下代碼,找到true後,for中的判斷!dp[k][i][j]不成立就跳出了。

class Solution {
public:
// 動態規劃 dp[k][i][j]存s1從第i個開始長度爲k的串和s2從j開始長度爲k的串是否scrambled
bool isScramble(string s1, string s2)
{
    if (s1.size() != s2.size()) return false;
    int len = s1.size();
    bool dp[len+1][len][len];
    
    // initialization
    for (int i = 0; i < len; ++i)
        for (int j = 0; j < len; ++j)
            dp[1][i][j] = s1[i] == s2[j];
            
    // dp
    for (int k = 2; k < len + 1; ++k)
        for (int i = 0; i < len; ++i)
            for (int j = 0; j < len; ++j)
            {
                // initialization
                dp[k][i][j] = false; 
                // once dp[k][i][j] is true, jump out
                for (int div = 1; div < k && !dp[k][i][j]; ++div)
                    dp[k][i][j] = (dp[div][i][j] && dp[k-div][i+div][j+div] || dp[div][i][j+k-div] && dp[k-div][i+div][j]);
            }
    return dp[len][0][0];
}
};

如下三點值得注意:

1. 此題遞歸只要主要判斷排序後子串是否相等,不等直接剪枝,減小計算量。

2. 兩中方法都用到的核心是把兩個字符串都分紅兩部分,若是對應匹配或者交叉匹配知足,則true。

3. 就是substr的用法,如何準確判斷子串。

相關文章
相關標籤/搜索