LC97: 交错字符串

使用动态规划解决LeetCode中的交错字符串问题,通过LCS思路,建立二维dp数组,根据状态转移方程判断s1和s2能否交错组成s3。进一步思考,可以优化为1D dp数组降低空间复杂度。
摘要由CSDN通过智能技术生成

问题描述

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

示例 1:

输入: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbcbcac”
输出: true
示例 2:

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

解题思路

这个题可以用动态规划解决,思路与LCS(longest common subsequence,最长公共子序列)类似。用 d p [ i ] [ j ] dp[i][j] dp[i][j] 来记录 s 1 [ : i ] , s 2 [ : j ] s1[:i], s2[:j] s1[:i],s2[:j]是否可以交错着组成 s 3 [ : i + j ] s3[:i+j] s3[:i+j]。注意,此处的index是 “up to but not included”,例如 s [ : i ] s[:i] s[:i]就是从开头选取该 string s长度为 i。

下一步就是写出状态转移方程,设想,如果我们要考察 s 1 [ : i ] , s 2 [ : j ] s1[:i], s2[:j] s1[:i],s2[:j]是否可以交错着组成 s 3 [ : i + j ] s3[:i+j] s3[:i+j],只需要查看:

  1. 是否 s 1 [ i − 1 ] = = s 3 [ i + j − 1 ] s1[i-1]==s3[i+j-1] s1[i1]==s3[i+j1] 如果确实为真,则问题可以被 reduce到考察 s 1 [ : i − 1 ] , s 2 [ : j ] s1[:i-1], s2[:j] s1[:i1],s2[:j]是否可以交错着组成 s 3 [ : i + j − 1 ] s3[:i+j-1] s3[:i+j1],其实就是 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j]的值,这就完成了问题的转换。
  2. 类似的,我们还可以考察是否 s 2 [ j − 1 ] = = s 3 [ i + j − 1 ] s2[j-1]==s3[i+j-1] s2[j1]==s3[i+j1] 如果确实为真,则问题可以被 reduce到考察 s 1 [ : i ] , s 2 [ : j − 1 ] s1[:i], s2[:j-1] s1[:i],s2[:j1]是否可以交错着组成 s 3 [ : i + j − 1 ] s3[:i+j-1] s3[:i+j1],其实就是 d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j1]的值。

综上,

dp[i][j] = (dp[i-1][j] * (s1[i-1]==s3[i+j-1]))  ||  (dp[i][j-1] * (s2[j-1]==s3[i+j-1]));

总的来说,可以写出如下代码:

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        int m = s1.size(), n = s2.size();
        if (m+n != s3.size()) return false;
        if (m==0) return s3 == s2;
        if (n==0) return s3 == s1;
        dp[i][j] is true if s1[:i] and s2[:j] interleaves s3[:i+j]  
        indexes are "up to but not included"
        vector< vector<int> >  dp(m+1, vector<int>(n+1, 0));
        // initialize dp
        dp[0][0] = 1;
        for (int i=1; i<=m; i++) {
            if (s1[i-1] == s3[i-1] ) dp[i][0] = dp[i-1][0];
        }
        for (int j=1; j<=n; j++) {
            if (s2[j-1] == s3[j-1] ) dp[0][j] = dp[0][j-1];
        }
        for (int i=1; i<=m; i++) {
            for (int j=1; j<=n; j++){
                dp[i][j] = (dp[i-1][j] * (s1[i-1]==s3[i+j-1]))  ||  (dp[i][j-1] * (s2[j-1]==s3[i+j-1]));
            }
        }
        return dp[m][n];
};

时间击败 90%,空间击败100%

进一步思考:使用 1D dp

如果我们再想一想状态转移方程,我们会发现,它其实只依赖于上方和左方的值,这提示我们其实可以使用一个 1D dp array就可以实现了。

具体代码如下:

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        int m = s1.size(), n = s2.size();
        if (m+n != s3.size()) return false;
        if (m==0) return s3 == s2;
        if (n==0) return s3 == s1;

        vector<int> dp(n+1, 0);
        dp[0] = 1;
        for (int j=1; j<=n; j++) {
            if (s2[j-1] == s3[j-1] ) dp[j] = dp[j-1];
        }
        for (int i=1; i<=m; i++){
            for (int j=0; j<=n; j++){
                if (j==0) dp[j] = dp[j] * (s1[i-1] == s3[i-1]);
                else dp[j] = (dp[j] * (s1[i-1]==s3[i+j-1]))  ||  (dp[j-1] * (s2[j-1]==s3[i+j-1]));
            }
        }
        return dp[n];
    }
};

动态规划嘛,就是如果想出了dp和状态转移就简单得跟什么似的,想不出就抓破脑袋…

参考

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值