LeetCode-扰乱字符串


题目:扰乱字符串

给定一个字符串 s1,我们可以把它递归地分割成两个非空子字符串,从而将其表示为二叉树。

下图是字符串 s1 = “great” 的一种可能的表示形式。

    great
   /    \
  gr    eat
 / \    /  \
g   r  e   at
           / \
          a   t

在扰乱这个字符串的过程中,我们可以挑选任何一个非叶节点,然后交换它的两个子节点。

例如,如果我们挑选非叶节点 “gr” ,交换它的两个子节点,将会产生扰乱字符串 “rgeat” 。

    rgeat
   /    \
  rg    eat
 / \    /  \
r   g  e   at
           / \
          a   t

我们将 “rgeat” 称作 “great” 的一个扰乱字符串。

同样地,如果我们继续交换节点 “eat” 和 “at” 的子节点,将会产生另一个新的扰乱字符串 “rgtae” 。

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

我们将 “rgtae” 称作 “great” 的一个扰乱字符串。

给出两个长度相等的字符串 s1 和 s2,判断 s2 是否是 s1 的扰乱字符串。

示例 1:

输入: s1 = "great", s2 = "rgeat"
输出: true

示例 2:

输入: s1 = "abcde", s2 = "caebd"
输出: false

来源:力扣(LeetCode)。链接:https://leetcode-cn.com/problems/scramble-string

解题思路:递归

由于无法得知原字符串分割的位置,所以通过蛮力遍历每一个位置对原字符串进行分割,然后比较原字符串和扰乱字符串分割之后的子串是否相等。子串同样进行了分割,则需要再对子串进行分割判断。这个时候就可以使用递归了。

由于分割之后还可能进行交换,所以需要判断两种情况:

  1. 直接对原字符串进行分割,比较原字符串和扰乱字符串分割之后的子串是否相等。即s1[0, i] == s2[0, i] && s1[i+1, len - 1] == s2[i+1, len -1]。
  2. 将分割后的两个子串交换,然后比较交换后的子串。s1[0, i] == s2[len-1 - i, len-1] && s1[i+1, len - 1] == s2[0, len-i]。

优化:
由于分割交换并不会改变字符串中字符的个数,所以可以通过统计并比较两字符串中各字符的个数是否相等做一次筛选。
如果不等,则一定不是扰乱字符串。这是一个 必要条件

LeetCode 运行结果

  • 执行用时 :8 ms, 在所有 C++ 提交中击败88.36的用户。
  • 内存消耗 :10 MB, 在所有 C++ 提交中击败了78.16%的用户。

代码

class Solution
{
public:
        bool isScramble(string s1, string s2)
        {
                if (s1.size() <= 1)
                &#123;
                        if (s1 == s2)
                        &#123;
                                return true;
                        &#125;
                &#125;
                else
                &#123;
                        return _isScramble(s1, s2);
                &#125;

                return false;
        &#125;

        bool _isScramble(string s1, string s2)
        &#123;                       
                int m = s1.length();
                if(s1 == s2)
                &#123;
                        return true;
                &#125;

                //统计s1和s2的字符个数,判断相应的字符个数是否相等。(必要条件)
                int char_num[26] = &#123;0&#125;;
                for(int i=0; i<m;i++)
                &#123;
                        char_num[s1[i] - 'a']++;
                        char_num[s2[i] - 'a']--;
                &#125;

                for(int i=0; i<26; i++)
                &#123;
                        if(char_num[i] != 0)
                        &#123;
                                return false;
                        &#125;
                &#125;

                for (int i = 0; i < m-1; i++)
                &#123;
                        //蛮力切割
                        if ((_isScramble(s1.substr(0, i+1), s2.substr(0, i+1)) &&
                        _isScramble(s1.substr(i + 1, m - (i + 1)), s2.substr(i + 1, m - (i + 1)))) == 1)
                        &#123;
                                return true;
                        &#125;

                        //切割并且交换
                        if ((_isScramble(s1.substr(0, i+1), s2.substr(m-(i+1), i+1)) &&
                        _isScramble(s1.substr(i + 1, m - (i + 1)), s2.substr(0, m - (i + 1)))) == 1)
                        &#123;
                                return true;
                        &#125;
                &#125;
                return false;
        &#125;
&#125;;

文章作者: Xu Yuan
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Xu Yuan !
评论
 上一篇
LeetCode-最长有效括号 LeetCode-最长有效括号
题目:最长有效括号给定一个只包含 ( 和 ) 的字符串,找出最长的包含有效括号的子串的长度。 示例 1: 输入: "(()" 输出: 2 解释: 最长有效括号子串为 "()"示例 2: 输入: &qu
2020-01-22
下一篇 
Linux 管道通信 Linux 管道通信
系统调用 fork在linux系统中创建进程有两种方式 一是由操作系统创建。 二是由父进程创建进程。系统调用函数fork()是创建一个新进程的唯一方式。 fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程。 系统先给新的
2019-11-14
  目录