不说废话,直戳盲点,打通思路, 举一反三。
题目描述
给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的。
输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
输出: true
输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
输出: false
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/interleaving-string
先上代码
执行用时 :12 ms, 在所有 C++ 提交中击败了61.34%的用户
内存消耗 :8.7 MB, 在所有 C++ 提交中击败了42.10%的用户
bool isInterleave(string s1, string s2, string s3) {
if(s1.size()+s2.size()!=s3.size())
return false;
vector<vector<bool> > dp(s1.size() + 1, vector<bool>(s2.size() + 1, false));
dp[0][0] = true;
for (int i = 1; i < s1.size() + 1; ++i) {
if (s1[i - 1] == s3[i - 1] && dp[i-1][0] == true)
dp[i][0] = true;
}
for(int j=1; j<s2.size()+1;++j){
if (s2[j - 1] == s3[j - 1] && dp[0][j-1] == true)
dp[0][j] = true;
}
for (int i = 1; i < s1.size()+1; ++i) {
for (int j = 1; j < s2.size()+1; ++j) {
if ( (dp[i - 1][j] == true && s1[i - 1] == s3[i + j - 1])
|| (dp[i][j - 1] == true && s2[j - 1] == s3[i + j - 1]) )
dp[i][j] = true;
else
dp[i][j] = false;
}
}
return dp[s1.size()][s2.size()];
}
算法思路
这是一个动态规划问题,所以我们要先得到状态转移方程。
- 定义状态 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示 s 1 [ 1 : i ] s_1[1:i] s1[1:i], s 2 [ 1 : j ] s_2[1:j] s2[1:j] 能否交错组成 s 3 [ 1 : i + j ] s_3[1:i+j] s3[1:i+j], 那么 d p [ n ] [ n ] dp[n][n] dp[n][n]就是我们想要的结果。
- 可得转移方程: d p [ i ] [ j ] = { T r u e d p [ i − 1 ] [ j ] = T r u e A N D s 1 [ i ] = s 3 [ i + j ] T r u e d p [ i ] [ j − 1 ] = T r u e A N D s 2 [ j ] = s 3 [ i + j ] F a l s e O t h e r s dp[i][j]=\left\{ \begin{array}{rcl} True & & {dp[i-1][j]=True}&{AND}&{s_1[i]=s_3[i+j]} \\ True & & {dp[i][j-1]=True}&{AND}&{s_2[j]=s_3[i+j]}\\ False & & {Others} \end{array} \right. dp[i][j]=⎩⎨⎧TrueTrueFalsedp[i−1][j]=Truedp[i][j−1]=TrueOthersANDANDs1[i]=s3[i+j]s2[j]=s3[i+j]
- 状态转移方程的第一条的含义可以这样理解, 如果 s 1 [ 1 : i − 1 ] s_1[1:i-1] s1[1:i−1]和 s 2 [ 1 : j ] s_2[1:j] s2[1:j]能交错组成 s 3 [ i + j − 1 ] s_3[i+j-1] s3[i+j−1], 那么当 s 1 [ i ] = s 3 [ i + j ] s_1[i]=s_3[i+j] s1[i]=s3[i+j]时,则 s 1 [ 1 : i ] s_1[1:i] s1[1:i]和 s 2 [ 1 : j ] s_2[1:j] s2[1:j]可以表示 s 3 [ 1 : i + j ] s_3[1:i+j] s3[1:i+j],这是递推为True的一个条件,第二条原理类似。
- 为了更好的表达状态转移方程是如何进行的,我们用表格表示它:
\ | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | T | F | ||||
1 | T | ? | ||||
2 | ||||||
3 | ||||||
4 | ||||||
5 |
值得注意的是,我们不能从 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0] 开始表示 s 1 [ 0 ] s_1[0] s1[0]和 s 2 [ 0 ] s_2[0] s2[0]能不能交错组成 s 3 [ 0 : 1 ] s_3[0:1] s3[0:1],因为字符应该一个一个的去加入考虑,如果从 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0]开始考虑,那么一开始就得考虑两个字符。为了方便的去表示, d p [ 0 ] [ 0 ] dp[0][0] dp[0][0]直接设置为True,表示 s 1 s_1 s1, s 2 s_2 s2都没有开始进入考虑时的状态,然后一切从 d p [ 0 ] [ 1 ] dp[0][1] dp[0][1]和 d p [ 1 ] [ 0 ] dp[1][0] dp[1][0]开始。
- 每个格子表示一个状态,再细看状态转移方程,两个条件分别是从当前格子的上方和左方去考虑,以 d p [ 1 ] [ 1 ] dp[1][1] dp[1][1]为例,两个条件分别考虑 d p [ 0 ] [ 1 ] dp[0][1] dp[0][1]和 d p [ 1 ] [ 0 ] dp[1][0] dp[1][0]是否为True下的情况,继而结合 s 1 [ 1 ] s_1[1] s1[1]或者 s 2 [ 1 ] s_2[1] s2[1]是否与 s 3 [ 1 ] s_3[1] s3[1]对应。所以,我们要想使得状态持续转移下去,每次计算状态的时候,要保证左方和上方的格子已经有结果。
- 我们可以很方便的得先到第一行,与第一列的结果,然后通过简单的嵌套循环就可以依次递推,详细的可以看代码。
学习和总结
- 二维状态不仅要弄清楚状态转移方程,更要弄清楚状态转移方程得以运行的机制。可以画表格来更加只管一些。
- 映像中相似的题目:通配符匹配.