87扰乱字符串(自顶向下递归法——困难)

1、题目描述

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

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

                  

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

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

2、示例

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

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

3、题解

基本思想:递归法,遍历所有可能的分割情况分割点i从1到s1.size-1,将s1分割成[0,i)[i,s1.size),对应s2匹配可能是[0,i)[i,s1.size)也可能是[s2.size-i,s2.size)[0,s2.size()-i),然后不断递归分割的子串直到s1=s2返回true。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Solution {
public:
    unordered_map<string,unordered_map<string,int>> Map;  //Map[s1][s2]=1表示s1和s2匹配,Map[s1][s2]=-1表示s1和s2不匹配,Map[s1][s2]=0表示s1和s2还没有递归搜索
	bool isScramble(string s1, string s2) {
		return Recursion(s1, s2);
	}
	bool Recursion(string s1, string s2)
	{
		if (s1 == s2)
        {
            Map[s1][s2]=1;
            return true;
        }	
		//防止超时,若s1中所有字符在s2中不都存在,说明s1无论怎么分割和s2无法匹配,直接返回false,大大优化效率
		string t1 = s1, t2 = s2;
		sort(t1.begin(), t1.end());
		sort(t2.begin(), t2.end());
		if (t1 != t2)
        {
            Map[s1][s2]=-1;
            return false;
        }
			
		//i从1到s1.size-1,不能从下标0开始,至少分割1个字符出来,否则无法跳出循环
		for (int i = 1; i < s1.size(); i++)
		{
			//将s1分割成s1l=[0,i)s1r=[i,s1.size)对应s2匹配可能是s2l=[0,i)s2r=[i,s1.size)也可能是s3l=[s2.size-i,s2.size)s3r=[0,s2.size()-i)
            string s1l=s1.substr(0, i),s1r=s1.substr(i,s1.size()-i);
            string s2l=s2.substr(0, i),s2r=s2.substr(i,s2.size()-i);
            string s3l=s2.substr(s2.size() - i, i),s3r=s2.substr(0, s2.size() - i);
            bool flag11,flag12,flag21,flag22,flag1,flag2;

            if(Map[s1l][s2l]==1)
                flag11=true;
            else if(Map[s1l][s2l]==-1)
                flag11=false;
            else
                flag11=Recursion(s1l, s2l);

            if(Map[s1r][s2r]==1)
                flag12=true;
            else if(Map[s1r][s2r]==-1)
                flag12=false;
            else
                flag12=Recursion(s1r, s2r);

            flag1=flag11&&flag12;
            if(flag1)
            {
                Map[s1][s2]=1;
                return true;
            }

            if(Map[s1l][s3l]==1)
                flag21=true;
            else if(Map[s1l][s3l]==-1)
                flag21=false;
            else
                flag21=Recursion(s1l, s3l);

            if(Map[s1r][s3r]==1)
                flag22=true;
            else if(Map[s1r][s3r]==-1)
                flag22=false;
            else
                flag22=Recursion(s1r, s3r);

            flag2=flag21&&flag22;
			if (flag2)
            {
                Map[s1][s2]=1;
                return true;
            }	
		}
        Map[s1][s2]=-1;
		return false;
	}
};
class Solution {
public:
	bool isScramble(string s1, string s2) {
		if (s1.size() != s2.size())
			return false;
		//基本思想:递归法,遍历所有可能的分割情况分割点i从1到s1.size-1,将s1分割成[0,i)[i,s1.size)
		//对应s2匹配可能是[0,i)[i,s1.size)也可能是[s2.size-i,s2.size)[0,s2.size()-i)
		//然后不断递归分割的子串直到s1=s2返回true
		return Recursion(s1, s2);
	}
	bool Recursion(string s1, string s2)
	{
		if (s1 == s2)
			return true;
		//防止超时,若s1中所有字符在s2中不都存在,说明s1无论怎么分割和s2无法匹配,直接返回false,大大优化效率
		string t1 = s1, t2 = s2;
		sort(t1.begin(), t1.end());
		sort(t2.begin(), t2.end());
		if (t1 != t2)
			return false;
		//i从1到s1.size-1,不能从下标0开始,至少分割1个字符出来,否则无法跳出循环
		for (int i = 1; i < s1.size(); i++)
		{
			//将s1分割成[0,i)[i,s1.size)对应s2匹配可能是[0,i)[i,s1.size)也可能是[s2.size-i,s2.size)[0,s2.size()-i)
			bool flag1 = Recursion(s1.substr(0, i), s2.substr(0, i)) && Recursion(s1.substr(i,s1.size()-i), s2.substr(i,s2.size()-i));
			bool flag2 = Recursion(s1.substr(0, i), s2.substr(s2.size() - i, i)) && Recursion(s1.substr(i, s1.size() - i), s2.substr(0, s2.size() - i));
			if (flag1 || flag2)
				return true;
		}
		return false;
	}
};
int main()
{
	Solution solute;
	string s1 = "abcd";
	string s2 = "cadb";
	cout << solute.isScramble(s1, s2) << endl;
	return 0;
}

 

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值