87. 扰乱字符串

题目描述

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.

方法1

思路

基于传统的递归方法,枚举S1每一种初始分割方式,一个个的递归判断(类似于树的镜像的判断方法),遇到成功的分割时要及时终止函数(只要有一种情况成功了就行了),简单的说,就是s1s2scramble的话,那么必然存在(只要成功就行了)一种分割,将s1分成s11s12两段,将s2分成s21s22.那么要么s11s21scramble的并且s12s22scramble的;s11s22scramble的并且s12s21scramble的。子问题是同样的问题,继续递归判断下去.

代码实现

class Solution {
public:
    bool isScramble(string s1, string s2)
     {
        if(s1.length()==0 && s2.length()==0)
          return true;
        if(s1.length()==0 || s2.length()==0)
          return false;
        if(s1.length() != s2.length())
          return false;
        if(s1 == s2)
          return true;
        //另外在进入递归前需要剪枝,判断两个字符串是否包含相同的字母,减少不必要的操作
        int c[26] = {0};
        for(int i = 0;i < s1.length();i++)
        {
          c[s1[i]-'a']++;
          c[s2[i]-'a']--;
        }
        for(int i=0;i<26;i++)
          if(c[i] != 0)
            return false;
        //枚举s1的每一种根节点的划分情况
        //i为划分左半部分的节点个数
        for(int i = 1;i < s1.length();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;       
    }

};

解法2

思路

动态规划方法
根据以往的经验来说,与符串有关的题十有八九可以用DP来做,动态规划只是把用递归函数方法中的重复的子问题,需要每次递归解决的用一个中间的数据结构存放临时信息,方便我们需要时直接得到结果。
我们维护量res[n][i][j],其中is1的起始字符下标,js2的起始字符下标,而n是当前的字符串长度,递归函数中的子问题s1s2可以是任意位置的子串,而确定一个子串只需要一个起点下标,一个长度即可.有什么样的子问题,就有什么样的dp数据结构设计.

代码实现

class Solution {
public:
    bool isScramble(string s1, string s2) 
    {

      if(s1.length()==0 && s2.length()==0)
          return true;
        if(s1.length()==0 || s2.length()==0)
          return false;
        if(s1.length() != s2.length())
          return false;
        if(s1 == s2)
          return true;

      int n = s1.size();
      vector<vector<vector<bool> > > dp(n+1, vector<vector<bool> >(n, vector<bool>(n,false)));
      for(int i = 0; i < n; ++i) 
      {
        for(int j = 0; j < n; ++j)  
          dp[1][i][j] = s1[i] == s2[j];  
      }
      for(int len = 2; len <= n; ++len) 
      {
        for(int i = 0; i <= n-len; ++i)
        {
          for(int j = 0; j <= n-len; ++j) 
          {
            //注意这里的递归公式中i,j求解的顺序没有要求,但是len的顺序必须从小到大
            for(int k = 1; k < len; ++k)
            {
              if((dp[k][i][j] && dp[len - k][i+k][j+k]) || (dp[k][i][j+len-k] && dp[len-k][i+k][j])) 
                {
                  dp[len][i][j] = true;
                  break;
                }
            }
              
          }

        }

      }

      return dp[n][0][0]; 

    }
};

posted on 2021-04-05 15:02  朴素贝叶斯  阅读(36)  评论(0编辑  收藏  举报

导航