数据机构与算法之深入解析“交错字符串”的求解思路与算法示例
Posted Serendipity·y
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据机构与算法之深入解析“交错字符串”的求解思路与算法示例相关的知识,希望对你有一定的参考价值。
一、题目要求
- 给定三个字符串 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 都由小写英文字母组成。
二、求解算法
① 动态规划
- 使用 dp[i][j] 表示 S1 的前 i 个字符和 S2 的前 j 个字符是否能构成 S3 的前 i+j 个字符,首先 dp[0][0] 一定是 True:
- 初始化 S1、S2、S3 的长度分别为 len1,len2,len3;
- 若 len1+len2!=len3,表示一定不能构成交错字符串,返回 False;
- 初始化 dp 为 (len1+1)∗(len2+1) 的 False 数组。
- 初始化第一列 dp[i][0],遍历第一列,遍历区间 [1,len1+1):
-
- dp[i][0]=dp[i−1][0]ands1[i−1]==s3[i−1],表示 S1 的前 i 位是否能构成 S3 的前 i 位。
-
- 因此需要满足的条件为,前 i−1 位可以构成 S3 的前 i−1 位且 S1 的第 i 位(s1[i−1])等于 S3 的第 i 位(s3[i−1])。
- 初始化第一行 dp[0][j],遍历第一行,遍历区间 [1,len2+1):
-
- dp[0][i]=dp[0][i−1]ands2[i−1]==s3[i−1],表示 S2 的前 i 位是否能构成 S3 的前 i 位。
-
- 因此需要满足的条件为,前 i−1 位可以构成 S3 的前 i−1 位且 S2 的第 i 位(s2[i−1])等于 S3 的第 i 位(s3[i−1])
- 遍历 dp 数组,每一行 i,遍历区间 [1,len1+1):每一列 j,遍历区间 [1,len2+1):
-
- dp[i][j]=(dp[i][j-1]\\ and\\ s2[j-1]==s3[i+j-1])\\ or\\ (dp[i-1][j]\\ and\\ s1[i-1]==s3[i+j-1])$$ 。解释: s 1 s1 s1 前 i i i 位和 s 2 s2 s2 的前 j j j 位能否组成 s 3 s3 s3 的前 i + j i+j i+j 位取决于两种情况:
-
- S1 的前 i 个字符和 S2 的前 j−1 个字符能否构成 S3 的前 i+j−1 位,且 S2 的第 j 位(s2[j−1])是否等于 S3 的第 i+j 位(s3[i+j−1])。
- 返回 dp[−1][−1]。
- Python 示例:
class Solution:
def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
len1=len(s1)
len2=len(s2)
len3=len(s3)
if(len1+len2!=len3):
return False
dp=[[False]*(len2+1) for i in range(len1+1)]
dp[0][0]=True
for i in range(1,len1+1):
dp[i][0]=(dp[i-1][0] and s1[i-1]==s3[i-1])
for i in range(1,len2+1):
dp[0][i]=(dp[0][i-1] and s2[i-1]==s3[i-1])
for i in range(1,len1+1):
for j in range(1,len2+1):
dp[i][j]=(dp[i][j-1] and s2[j-1]==s3[i+j-1]) or (dp[i-1][j] and s1[i-1]==s3[i+j-1])
return dp[-1][-1]
② 动态规划(LeetCode 官方解法)
- 记 ∣s1∣=n,∣s2∣=m。
- 双指针法错在哪里? 也许我们第一反应是使用双指针法解决这个问题,指针 p1 一开始指向 s1 的头部,指针 p2一开始指向 s2 的头部,指针 p3 指向 s3 的头部,每次观察 p1 和 p2 指向的元素哪一个和 p3 指向的元素相等,相等则匹配并后移指针。样例就是一个很好的反例,用这种方法判断 s1 = aabcc,s2 = dbbca,s3 = aadbbcbcac 时,得到的结果是 False,实际应该是 True。
- 解决这个问题的正确方法是动态规划:
-
- 首先如果 ∣s1∣ + ∣s2∣ = ∣s3∣,那 s3 必然不可能由 s1 和 s2 交错组成。在 ∣s1∣ + ∣s2∣ = ∣s3∣ 时,可以用动态规划来求解。
-
- 定义 f(i,j) 表示 s1 的前 i 个元素和 s2 的前 j 个元素是否能交错组成 s3 的前 i+j 个元素;
-
- 如果 s1 的第 i 个元素和 s3 的第 i+j 个元素相等,那么 s1 的前 i 个元素和 s2 的前 j 个元素是否能交错组成 s3 的前 i+j 个元素取决于 s1 的前 i−1 个元素和 s2 的前 j 个元素是否能交错组成 s3 的前 i+j−1 个元素,即此时 f(i,j) 取决于 f(i−1,j),在此情况下如果 f(i−1,j) 为真,则 f(i,j) 也为真。
-
- 同样的,如果 s2 的第 j 个元素和 s3 的第 i+j 个元素相等并且 f(i,j−1) 为真,则 f(i,j) 也为真,于是可以推导出这样的动态规划转移方程:
- 其中 p=i+j−1,边界条件为 f(0,0)=True。
- Java 示例:
class Solution
public boolean isInterleave(String s1, String s2, String s3)
int n = s1.length(), m = s2.length(), t = s3.length();
if (n + m != t)
return false;
boolean[][] f = new boolean[n + 1][m + 1];
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][j] || (f[i - 1][j] && s1.charAt(i - 1) == s3.charAt(p));
if (j > 0)
f[i][j] = f[i][j] || (f[i][j - 1] && s2.charAt(j - 1) == s3.charAt(p));
return f[n][m];
- C++ 示例:
class Solution
public:
bool isInterleave(string s1, string s2, string s3)
auto f = vector < vector <int> > (s1.size() + 1, vector <int> (s2.size() + 1, false));
int n = s1.size(), m = s2.size(), t = s3.size();
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];
;
以上是关于数据机构与算法之深入解析“交错字符串”的求解思路与算法示例的主要内容,如果未能解决你的问题,请参考以下文章
数据机构与算法之深入解析“柱状图中最大的矩形”的求解思路与算法示例
数据机构与算法之深入解析“复原IP地址”的求解思路与算法示例