• leetcode[86] Scramble String


    将一个单词按照这种方式分:

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

    这样就说他们是scrambled string。现在给你两个字符串怎么判断是否属于scrambled string呢

    思路:

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

    然后学习了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的用法,如何准确判断子串。

  • 相关阅读:
    电脑hosts文件、hosts文件说明、hosts文件域名和ip
    java复制对象属性值、复制值
    查找替换、idea全局搜索、全局替换、全局搜索替换
    谷歌浏览器问题、
    http请求类、RestTemplate、请求工具类
    easypoi导入
    vue下载本地文件、vue下载本地文件报错、vue下载本地文件找不到
    arm汇编指令的条件码
    GNU内嵌汇编
    shell脚本错误:syntax error near unexpected token '$' ''
  • 原文地址:https://www.cnblogs.com/higerzhang/p/4111820.html
Copyright © 2020-2023  润新知