学渣带你刷Leetcode0087扰乱字符串

题目描述

给定一个字符串 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;
}
*/

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值