leetcode每日一题 --- 交错字符串

给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的。

示例1:

输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
输出: true

示例2:

输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
输出: false

这道题目标注为“困难”显然是有一定的道理的

误区: 1)第一眼看到这道题时,由于没有认真读示例,题干也比较简略,所以没有注意到这里的“交错”的含义,直接当做一个简单的字符匹配问题,认为只要s1和s2的字符和s3能完全匹配就可以,当然这是不对的qaq;

2)双指针方法:用两个指针标记s1和s2,交替匹配,看起来很可行,但还是存在一定的问题,比如错误匹配对象导致结果错误。
举个例子:aabcc dbbca aadbbcbcac
对于共有的b,不知道是匹配s1还是s2,任何一步匹配选择错误都会让结果出错,所以单纯的双指针不太可行。当然你可以运用回溯法,每次匹配错误就回溯,直到尝试了所有可能,这个我们后面再讨论。

题解:
1)动态规划算法
因为正向匹配很难确定相同字符如何匹配,所以我们转换思路,从末尾开始推演匹配。假设s1的最后一个字符与s3的最后一个字符相同,则问题就等价于s1的len1 - 1个字符和s2的len2个字符可以与s3的len3 - 1个字符匹配的问题(s2的情况类似)…最后推演到两个空字符,必定匹配。

我们需要确定一个动态规划转移方程,这里需要确定的是,如果len1 + len2 不等于len3,则不可能匹配成功,所以排除这种情况:
假设有一个函数f[i] [j] 表示s1的前i个字符加上s2的前j个字符可以与s3的前i + j 个字符匹配,由上面的推演我们可以得到
f[i][j] = (f[i][j-1] && s2[j] == s3[i + j - 1]) || (f[i-1][j] && s1[i] == s3[i + j - 1])

题解:

bool isInterleave(char * s1, char * s2, char * s3){
    //注意题目要求,必须是s1,s2“交错”组成s3,不是简单地字符匹配就可以
    int n = strlen(s1), m = strlen(s2), t = strlen(s3);

    int f[n + 1][m + 1]; //注意这里的数组大小
    memset(f, 0, sizeof(f)); //初始化为0

    if (n + m != t) {
        return false;
    }

    f[0][0] = true; //边界条件
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            int p = i + j - 1;
            if (i > 0) {
                f[i][j] = (f[i - 1][j] && s1[i - 1] == s3[p]); //保证相同字符可以有两次匹配机会
            }                                                   //所以这里是或等于,有一种匹配即可
            if (j > 0) {    
                f[i][j] |= (f[i][j - 1] && s2[j - 1] == s3[p]);
            }
        }
    }

    return f[n][m];
}

这里有一个很神奇的点,只是把 if (i > 0) 后执行的语句由或等于改成了直等于,执行时间就由4ms变成了0ms。第二个if(j > 0)后的执行语句采用或等于的原因是为了防止第一次匹配而第二次不匹配,结果错认为不匹配,因此不能采用直等于,而采用或等于,只有有一次匹配结果即为1。
在这里插入图片描述
也可以用滚动数组法进一步优化空间复杂度,由于二维数组中第i行仅与i - 1行有关,因此我们可以只维护一行,即用一个一维数组f[m + 1]来存储结果。代码改动比较简单,这里不展示了。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/interleaving-string/
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值