Leetcode97 交错字符串 C++,Java,Python

Leetcode97 交错字符串

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/interleaving-string/submissions/

博主Githubhttps://github.com/GDUT-Rp/LeetCode

题目:

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

示例 1:

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

示例 2:

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

解题思路:

方法一:使用二维动态规划

直观想法

上面提到的回溯方法包含每次从 s 1 s1 s1 或者 s 2 s2 s2 中选择一个字符并调用递归函数去检查 s 1 s1 s1 s 2 s2 s2 剩余部分能否形成 s 3 s3 s3 剩余部分的交错字符串。在现在这种方法中,我们用另一种思路来考虑同样的问题。这里我们考虑用 s 1 s1 s1 s 2 s2 s2 的某个前缀是否能形成 s 3 s3 s3 的一个前缀。

这个方法的前提建立于:判断一个 s 3 s3 s3 的前缀(用下标 k k k 表示),能否用 s 1 s1 s1 s 2 s2 s2 的前缀(下标分别为 i i i j j j),仅仅依赖于 s 1 s1 s1 i i i 个字符和 s 2 s2 s2 j j j 个字符,而与后面的字符无关。

为了实现这个算法, 我们将使用一个 2D 的布尔数组 d p dp dp d p [ i ] [ j ] dp[i][j] dp[i][j] 表示用 s 1 s1 s1 的前 ( i + 1 ) (i+1) (i+1) s 2 s2 s2 的前 ( j + 1 ) (j+1) (j+1) 个字符,总共 ( i + j + 2 ) (i+j+2) (i+j+2) 个字符,是否交错构成 s 3 s3 s3 的前缀。为了求出 d p [ i ] [ j ] dp[i][j] dp[i][j] ,我们需要考虑 2 2 2 种情况:

s 1 s1 s1 的第 i i i 个字符和 s 2 s2 s2 的第 j j j 个字符都不能匹配 s 3 s3 s3 的第 k k k 个字符,其中 k = i + j + 1 k=i+j+1 k=i+j+1 。这种情况下, s 1 s1 s1 s 2 s2 s2 的前缀无法交错形成 s 3 s3 s3 长度为 k + 1 k+1 k+1 的前缀。因此,我们让 d p [ i ] [ j ] dp[i][j] dp[i][j] F a l s e False False

s 1 s1 s1 的第 i i i 个字符或者 s 2 s2 s2 的第 j j j 个字符可以匹配 s 3 s3 s3 的第 k k k 个字符,其中 k = i + j + 1 k=i+j+1 k=i+j+1 。假设匹配的字符是 x x x 且与 s 1 s1 s1 的第 i i i 个字符匹配,我们就需要把 x x x 放在已经形成的交错字符串的最后一个位置。此时,为了我们必须确保 s 1 s1 s1 的前 ( i − 1 ) (i-1) (i1) 个字符和 s 2 s2 s2 的前 j j j 个字符能形成 s 3 s3 s3 的一个前缀。类似的,如果我们将 s 2 s2 s2 的第 j j j 个字符与 s 3 s3 s3 的第 k k k 个字符匹配,我们需要确保 s 1 s1 s1 的前 i i i 个字符和 s 2 s2 s2 的前 ( j − 1 ) (j-1) (j1) 个字符能形成 s 3 s3 s3 的一个前缀,我们就让 d p [ i ] [ j ] dp[i][j] dp[i][j] T r u e True True

可以用下面的例子进行说明:

C++
#include <iostream>
#include <vector>
#include <string>

class Solution_LeetCode97 {
public:
    bool isInterleave(string s1, string s2, string s3) {
        if (s3.length() != s1.length() + s2.length()) {     //  长度不匹配
            return false;
        }
        bool dp[s1.length() + 1][s2.length() + 1];
        for (int i = 0; i <= s1.length(); ++i) {
            for (int j = 0; j <= s2.length(); ++j) {
                if (i == 0 && j == 0) {
                    dp[i][j] = true;
                } else if (i == 0) {
                    dp[i][j] = dp[i][j - 1] && (s2[j - 1] == s3[i + j - 1]);
                } else if (j == 0) {
                    dp[i][j] = dp[i - 1][j] && (s1[i - 1] == s3[i + j - 1]);
                } else {
                    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[s1.length()][s2.length()];
    }
};
Java
public class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        if (s3.length() != s1.length() + s2.length()) {
            return false;
        }
        boolean dp[][] = new boolean[s1.length() + 1][s2.length() + 1];
        for (int i = 0; i <= s1.length(); i++) {
            for (int j = 0; j <= s2.length(); j++) {
                if (i == 0 && j == 0) {
                    dp[i][j] = true;
                } else if (i == 0) {
                    dp[i][j] = dp[i][j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1);
                } else if (j == 0) {
                    dp[i][j] = dp[i - 1][j] && s1.charAt(i - 1) == s3.charAt(i + j - 1);
                } else {
                    dp[i][j] = (dp[i - 1][j] && s1.charAt(i - 1) == s3.charAt(i + j - 1)) || (dp[i][j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1));
                }
            }
        }
        return dp[s1.length()][s2.length()];
    }
}
复杂度分析

时间复杂度: O ( m ⋅ n ) O(m \cdot n) O(mn) 。计算 d p dp dp 数组需要 m ∗ n m*n mn 的时间。

空间复杂度: O ( m ⋅ n ) O(m \cdot n) O(mn)。2 维的 d p dp dp 数组需要 ( m + 1 ) ∗ ( n + 1 ) (m+1)*(n+1) (m+1)(n+1) 的空间。 m m m n n n 分别是 s 1 s1 s1 s 2 s2 s2 字符串的长度。

方法二:使用一维动态规划

直观想法

这种方法与前一种方法基本一致,除了我们仅使用一维 d p dp dp 数组去储存前缀结果。我们利用 d p [ i − 1 ] dp[i-1] dp[i1] 的结果和 d p [ i ] dp[i] dp[i] 之前的结果来计算 d p [ i ] dp[i] dp[i] ,即滚动数组。

C++
bool isInterleave_one(string s1, string s2, string s3) {
    if (s3.length() != s1.length() + s2.length()) {     //  长度不匹配
        return false;
    }
    bool dp[s2.length() + 1];
    for (int i = 0; i <= s1.length(); ++i) {
        for (int j = 0; j <= s2.length(); ++j) {
            if (i == 0 and j == 0) {
                dp[j] = true;
            } else if (i == 0) {
                dp[j] = dp[j - 1] && (s2[j - 1] == s3[i + j - 1]);
            } else if (j == 0) {
                dp[j] = dp[j] && (s1[i - 1] == s3[i + j - 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[s2.length()];
}
Java
public class Solution {
   public boolean isInterleave(String s1, String s2, String s3) {
       if (s3.length() != s1.length() + s2.length()) {
           return false;
       }
       boolean dp[] = new boolean[s2.length() + 1];
       for (int i = 0; i <= s1.length(); i++) {
           for (int j = 0; j <= s2.length(); j++) {
               if (i == 0 && j == 0) {
                   dp[j] = true;
               } else if (i == 0) {
                   dp[j] = dp[j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1);
               } else if (j == 0) {
                   dp[j] = dp[j] && s1.charAt(i - 1) == s3.charAt(i + j - 1);
               } else {
                   dp[j] = (dp[j] && s1.charAt(i - 1) == s3.charAt(i + j - 1)) || (dp[j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1));
               }
           }
       }
       return dp[s2.length()];
   }
}
复杂度分析

时间复杂度: O ( m ⋅ n ) O(m \cdot n) O(mn) 。计算 d p dp dp 数组需要 m ∗ n m*n mn 的时间。

空间复杂度: O ( n ) O(n) O(n)n 是字符串 s 1 s1 s1的长度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值