每日一解 交错字符串(不算困难的动态规划)

标题 交错字符串

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

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

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

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

思路

第一反应自然是觉得用几个指针就可以很好地处理了。三个指针分别从三个字符串的初始往后走,遇到S1和S2当前的字母都不能和S3匹配的情况,就返回false。感觉时间复杂度是O(n),空间复杂度为常数,完全可以接受,那就按照这个思路先编码。
于是果不其然发现了思路的漏洞。例如出现使用"cc"和"ca"去匹配"cacc"的情况。那么一定是先匹配了ca再匹配cc。倘若先匹配了cc中的第一个c,那么接下来会发现无法匹配,返回false。事实上正确答案应该是true。
那么针对这种情况,很明显还是要是用动态规划来解决问题。我个人在思考匹配模式的时候,想到了采用如下的(N + 1) ✖ (M + 1)表格来及进行求解(N和M分别为s1和s2的长度):
表中的例子为示例1中的S1 = aabcc,S2 = dbbca, 求解目标字符串S3 = aadbbcbcac。表中第一行为S1,第一列为S2。

a a b c c
0 1 2 2 2 2
d 0 1 3 4 4 4
b 0 1 4 5 5 5
b 0 1 5 6 7 8
c 0 1 5 7 8 8
a 1 2 5 7 9 10

表中的数字是在该行该列的字符串情况下能匹配到的最大长度。
就用表中标记出来的4和7作为例子,
先看4:4上方的3大于左侧的1,那么说明接下来要匹配的是左侧的b,然后发现S3[3]为b,可以匹配,那么将该格置为3 + 1 = 4。即aa和db可以匹配到原表中的第4个字母。
然后看7,7左侧的6大于上方的5,那么说明接下来要匹配的是上方的c,然后发现S3[7]为c,可以匹配,那么将该格置为6 + 1 = 7。即aabc和dbb可以匹配到原表中的第7个字母。
最终可以发现表末的字母为10,正好是S3的长度,则认为匹配成功,可以满足要求。如果不足10,那么说明不可以匹配(注意表中加粗斜体的2,事实上是不能如此匹配的‘使用a和dbbca匹配到第2位’。但考虑到这样是跳过了一些字母的,会导致最终表末结果不到10,那么也就不用担心会造成错误结果了)。

代码实现

class Solution {
    
    
public:
	bool isInterleave(string s1, string s2, string s3) {
    
    
		if (s1.size() + s2.size() != s3.size()) {
    
    
			return false;
		}
		vector<vector<int>> dp_map(s1.size() + 1, vector<int>(s2.size() + 1, 0));
		for (int i = 1; i <= s2.size(); i++) {
    
    
			s2[i - 1] == s3[dp_map[0][i - 1]] ? dp_map[0][i] = dp_map[0][i - 1] + 1 : dp_map[0][i] = dp_map[0][i - 1];
		}
		for (int i = 1; i <= s1.size(); i++) {
    
    
			s1[i - 1] == s3[dp_map[i - 1][0]] ? dp_map[i][0] = dp_map[i - 1][0] + 1 : dp_map[i][0] = dp_map[i - 1][0];
		}
		for (int i = 1; i <= s1.size(); i++) {
    
    
			for (int j = 1; j <= s2.size(); j++) {
    
    
				if (dp_map[i - 1][j] > dp_map[i][j - 1]) {
    
    
					s1[i - 1] == s3[dp_map[i - 1][j]] ? dp_map[i][j] = dp_map[i - 1][j] + 1 : dp_map[i][j] = dp_map[i - 1][j];
				}
				else if (dp_map[i - 1][j] < dp_map[i][j - 1]) {
    
    
					s2[j - 1] == s3[dp_map[i][j - 1]] ? dp_map[i][j] = dp_map[i][j - 1] + 1 : dp_map[i][j] = dp_map[i][j - 1];
				}
				else {
    
    
					if (s1[i - 1] == s3[dp_map[i - 1][j]]) {
    
    
						dp_map[i][j] = dp_map[i - 1][j] + 1;
					}
					else if (s2[j - 1] == s3[dp_map[i][j - 1]]) {
    
    
						dp_map[i][j] = dp_map[i][j - 1] + 1;
					}
					else {
    
    
						dp_map[i][j] = dp_map[i][j - 1];
					}
				}
			}
		}
		return dp_map[s1.size()][s2.size()] == s3.size();
	}
};

值得注意的小tip就是当左侧和上方值一样大的时候,就要同时匹配上方和左侧的字母看是否符合条件。以及当s1和s2的长度之和与s3不符的时候,直接扔出false即可。

猜你喜欢

转载自blog.csdn.net/new_DYX/article/details/107424289