LeetCode 面试经典 150_多维动态规划_交错字符串(97_C++_中等)

LeetCode 面试经典 150_多维动态规划_交错字符串(97_C++_中等)

题目描述:

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

两个字符串 s 和 t 交错 的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:

  • s = s1 + s2 + ... + sn
  • t = t1 + t2 + ... + tm
  • |n - m| <= 1
  • 交错 是 s1 + t1 + s2 + t2 + s3 + t3 + ... 或者 t1 + s1 + t2 + s2 + t3 + s3+...
  • 注意:a + b 意味着字符串 a 和 b 连接。

输入输出样例:

示例 1:

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

输出:true

示例 2:

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

输出:false

示例 3:

输入 :s1 = "", s2 = "", s3 = ""

输出:true

提示:

0 <= s1.length, s2.length <= 100

0 <= s3.length <= 200

s1、s2、和 s3 都由小写英文字母组成

题解:

解题思路:

思路一(动态规划):

1、解题思路

① 此题基本大意为:给你三个字符串 s1、s2、s3。把 s1 的字符顺序保持不变 、s2 的字符顺序保持不变 ,然后把它们穿插合并在一起,拼出来是否刚好等于 s3。

② 首先可能会想到双指针 的方法来求解,但是遇到相同字符二选一的时候,选错分支,后面直接翻车 。

③ 此题的解题非常像 LCS(最长公共子序列)

④ 具体解决思路如下:

  • s1.size() + s2.size() != s3.size() → 直接 false,长度对不上一定交错不了
  • 状态定义:dp[i][j]:用 s1 的前 i 个字符、s2 的前 j 个字符,能不能拼成 s3 的前 i+j 个字符
    • i:s1 取了 i 个(s1 0~s1 i-1)
    • j:s2 取了 j 个(s2 0~s2 j-1)
    • 此时 s3 用到的位置下标:p = i + j - 1
  • dp[0][0] = true:s1 取 0 个、s2 取 0 个,空串可以拼成空串(其余为false)
  • 只拿 s1、不取 s2:dp[i][0] = dp[i-1][0] && s1[i-1]==s3[i-1]
  • 只拿 s2、不取 s1:do[0][j] = dp[0][j-1] && s2[j-1]==s3[j-1]
  • 最后一个字符来自 s1 或 s2
    • dpij = (dpi-1j && s1i-1==s3i+j-1) || (dpij-1 && s2j-1==s3i+j-1);
  • dp[n][m],n 是 s1 总长,m 是 s2 总长

二维 dp 数组中,当前位置可以由左侧位置 (dpij-·) 和 上侧位置 (dpi-1j) 决定的时候可以考虑使用一维 dp 数组来优化 。

2、复杂度分析:

① 时间复杂度:O(n × m),遍历了一个二维dp 数组

② 空间复杂度:O(n × m),使用了二维dp数组

代码实现

代码实现(思路一(动态规划:二维dp)):
cpp 复制代码
class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        // n:s1长度,m:s2长度,t:s3长度
        int n = s1.size(), m = s2.size(), t = s3.size();

        // 剪枝:s1和s2字符总数不等于s3,一定无法交错组成
        if (n + m != t) return false;

        /*
         * dp[i][j] 含义:
         * 使用 s1 的前 i 个字符、s2 的前 j 个字符,能否匹配 s3 的前 i+j 个字符
         */
        vector<vector<bool>> dp(n + 1, vector<bool>(m + 1, false));
        // 0个s1 + 0个s2,可以拼成空串s3,初始为true
        dp[0][0] = true;

        // 初始化:只拿s1,不取s2的情况
        for (int i = 1; i <= n; ++i) {
            // 前面i-1个字符合法,并且当前s1字符和s3对应位置相等
            dp[i][0] = dp[i - 1][0] && (s1[i - 1] == s3[i - 1]);
        }

        // 初始化:只拿s2,不取s1的情况
        for (int j = 1; j <= m; ++j) {
            // 前面j-1个字符合法,并且当前s2字符和s3对应位置相等
            dp[0][j] = dp[0][j - 1] && (s2[j - 1] == s3[j - 1]);
        }

        // 双重循环:同时使用s1和s2的字符
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                // s3当前对比的下标:一共取了i+j个字符,下标从0开始,所以-1
                int p = i + j - 1;
                // 两种可能:
                // 1. 最后一个字符来自s1:dp[i-1][j]合法,s1当前字符等于s3[p]
                // 2. 最后一个字符来自s2:dp[i][j-1]合法,s2当前字符等于s3[p]
                dp[i][j] = (dp[i - 1][j] && s1[i - 1] == s3[p]) || (dp[i][j - 1] && s2[j - 1] == s3[p]);
            }
        }

        // 取完s1全部n个、s2全部m个字符,是否能拼成s3
        return dp[n][m];
    }
};
代码实现(思路二(动态规划:一维dp)):
cpp 复制代码
class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        int n = s1.size(), m = s2.size(), t = s3.size();
        // 剪枝:总长度不相等直接返回false
        if (n + m != t) return false;

        /*
         * 一维dp[j]含义:
         * 当前已经用了 s1 的前 i 个字符,s2 的前 j 个字符,能否匹配 s3 的前 i+j 个字符
         * 因为二维dp[i][j]只依赖dp[i-1][j](上一轮旧值)和dp[i][j-1](本轮刚更新的值)
         * 所以可以压缩成一维数组,滚动更新
         */
        vector<bool> dp(m + 1, false);
        // i=0,j=0:不取s1不取s2,空串匹配空串
        dp[0] = true;

        // 初始化i=0,只取s2的情况
        for (int j = 1; j <= m; ++j) {
            dp[j] = dp[j - 1] && (s2[j - 1] == s3[j - 1]);
        }

        // 遍历s1,i代表取s1前i个字符
        for (int i = 1; i <= n; ++i) {
            // dp[0]:j=0,不取s2,只取s1前i个字符
            dp[0] = dp[0] && (s1[i - 1] == s3[i - 1]);

            // 遍历s2,j代表取s2前j个字符
            for (int j = 1; j <= m; ++j) {
                int p = i + j - 1; // s3当前对比下标
                // 两种来源:
                // 1. 上一轮的dp[j]:也就是原来的dp[i-1][j],最后一个字符来自s1
                // 2. 本轮刚更新的dp[j-1]:也就是原来的dp[i][j-1],最后一个字符来自s2
                dp[j] = (dp[j] && s1[i - 1] == s3[p]) || (dp[j - 1] && s2[j - 1] == s3[p]);
            }
        }

        return dp[m];
    }
};

LeetCode 面试经典 150_多维动态规划_交错字符串(97_C++)原题链接

欢迎大家和我沟通交流(✿◠‿◠)

相关推荐
心抵鹊36 分钟前
算法之双指针系列(一)
算法·leetcode
十二1 小时前
【洛谷题解】P1083 [NOIP 2012 提高组] 借教室(二分答案 + 差分)
数据结构·c++·算法·蓝桥杯
小此方2 小时前
[算法实战论:动态规划]回文串问题的dp解法,回文串问题的母题,以及以此为基础的派生题
算法·动态规划
Pniubi2 小时前
力扣560.和为k的子数组
算法·leetcode·职场和发展
D e re k ZH3 小时前
C++ 从零入门(一):基础语法与程序结构
数据结构·c++·算法
Pniubi3 小时前
力扣438.找到字符串中所有字母异位词
算法·leetcode·职场和发展
yeccheng4 小时前
MySQL 索引超详解:从底层 B+ 树到面试高频考点
java·开发语言·jvm·mysql·面试
東隅已逝,桑榆非晚6 小时前
算法6. 双指针法-查找总价格为目标的两个商品
c++·学习·算法
Eyfcom6 小时前
〖物联网终端实战 01〗UWB 室内高精度定位:仓储 AGV 与人员的厘米级轨迹追踪
物联网·算法·物流系统·快递系统·跨境物流快递系统·物联网终端实战