题目描述
给定一个字符串 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
白话题目:
算法:
详细解释关注 B站 【C语言全代码】学渣带你刷Leetcode 不走丢 https://www.bilibili.com/video/BV1C7411y7gB
C语言完全代码
//方法二:动态规划算法
//1,递归是自顶向下处理,不断将问题分解递归,复杂度高,超时
//2,用数组保存递归过程中的值,将处理问题的方式改为 自底向上处理
//3,把 自顶向下 改成 自底向上 需要解决如何找到最小值的问题,即最先计算什么
//4,递归函数传递过程中处理了三个元素 (i, j, len) 分别为 s1下标,s2下标,分割长度
//5,使用动态数组 dp[i][j][l] 表示 &s1[i] 和 &s2[j] 长度为 l 个字符是否为扰乱字符串
//6,计算初始值 当 l=1 时 if(s1[i]==s2[j]) dp[i][j][1]=true
//7,三维的动态规划数组不太好理解,可以先理解几个例子
/*
以 great 和 rgeat 为例
例1:
当 l == 2 时
dp[0][0][2] = (dp[0][0][1] && dp[1][1][1]) || (dp[0][1][1] && dp[1][0][1])
...
dp[3][3][2] = (dp[3][3][1] && dp[4][4][1]) || (dp[3][4][1] && dp[4][3][1])
当 l == 3 时
dp[0][0][3] = ((dp[0][0][1] && dp[1][1][2]) || (dp[0][2][1] && dp[1][0][2])) ||
((dp[0][0][2] && dp[2][2][1]) || (dp[0][1][2] && dp[2][0][1]))
...
dp[2][2][3] = ((dp[2][2][1] && dp[3][3][2]) || (dp[2][4][1] && dp[3][2][2])) ||
((dp[2][2][2] && dp[4][4][1]) || (dp[2][3][2] && dp[4][2][1]))
*/
bool isScramble(char * s1, char * s2){
int i = 0;
int j = 0;
int l = 0;
int k = 0;
int iLenS1 = strlen(s1);
int iLenS2 = strlen(s2);
bool bRet = false;
bool dp[iLenS1][iLenS2][iLenS1 + 1];
if((NULL == s1) || (NULL == s2) || (iLenS1 != iLenS2)) return false;
//1,初始化动态规划数组
memset(dp, 0x00, sizeof(bool) * iLenS1 * iLenS2 * (iLenS1 + 1));
for(i = 0; i < iLenS1; i++)
{
for(j = 0; j < iLenS2; j++)
{
if(s1[i] == s2[j])
{
dp[i][j][1] = true;
}
}
}
//2,动态规划处理
for(l = 2; l <= iLenS1; l++)
{
//依次处理分割长度
for(i = 0; i <= iLenS1 - l; i++)
{
for(j = 0; j <= iLenS2 - l; j++)
{
//计算 dp[i][j][l] 的值, 需要判断 (1, l) 范围任一分割长度是否能判定为扰乱字符串
for(k = 1; k < l; k++)
{
// printf("[1][i=%d][j=%d][l=%d][k=%d][%d][%d]\n", i, j, l, k, dp[i][j][k], dp[i + k][j + k][l - k]);
//判断不交换节点
if((dp[i][j][k]) && (dp[i + k][j + k][l - k]))
{
dp[i][j][l] = true;
break;
}
// printf("[2][i=%d][j=%d][l=%d][k=%d][%d][%d]\n", i, j, l, k, dp[i][j + l - k][k], dp[i + k][j][l - k]);
//判断交换节点
if((dp[i][j + l - k][k]) && (dp[i + k][j][l - k]))
{
dp[i][j][l] = true;
break;
}
}
}
}
}
return dp[0][0][iLenS1];
}
/*
//方法一:递归法(超时)
//1,求最终解 fun(s1(0, len), s2(0, len)) 表示字符串s1,和s2是否匹配为扰乱字符串
//2,递归算法
//3,按照字符串长度遍历 k = (1, len)
//4, fun(s1(0, len), s2(0, len)) =
// (fun(s1(0, k), s2(0, k)) && fun(s1(k, len), s2(k, len))) ||
// (fun(s1(0, k), s2(len - k, len)) && fun(s1(k, len), s2(0, len - k)))
//递归函数
bool recursiveScramble(char* s1, char* s2, int len){
int i = 0;
bool bRet = false;
// printf("[1][s1=%s][s2=%s][len=%d]\n", s1, s2, len);
//1,结束处理
if(len == 1)
{
if(s1[0] == s2[0]) return true;
}
else
{
//2,遍历长度回溯处理
for(i = 1; i < len; i++)
{
//不交换处理
bRet = recursiveScramble(&s1[0], &s2[0], i);
// printf("[2][s1=%s][s2=%s][i=%d][ret=%d]\n", &s1[0], &s2[0], i, bRet);
if(bRet)
{//great
bRet &= recursiveScramble(&s1[i], &s2[i], len - i);
// printf("[3][s1=%s][s2=%s][len=%d][ret=%d]\n", &s1[i], &s2[i], len - i, bRet);
if(bRet) return true;
}
//交换处理
bRet = recursiveScramble(&s1[0], &s2[len - i], i);
// printf("[4][s1=%s][s2=%s][len=%d][ret=%d]\n", &s1[0], &s2[len - i], i, bRet);
if(bRet)
{//great
bRet &= recursiveScramble(&s1[i], &s2[0], len - i);
// printf("[5][s1=%s][s2=%s][len=%d][ret=%d]\n", &s1[i], &s2[0], len - i, bRet);
if(bRet) return true;
}
}
}
return bRet;
}
bool isScramble(char * s1, char * s2){
int iLenS1 = strlen(s1);
int iLenS2 = strlen(s2);
bool bRet = false;
if((NULL == s1) || (NULL == s2) || (iLenS1 != iLenS2)) return false;
bRet = recursiveScramble(s1, s2, iLenS1);
return bRet;
}
*/