一、经验总结
- 状态表示:两个数组的dp问题自然是要创建二维dp表,分别对应两个数组中的下标
- 分析状态转移方程:根据动态规划过程中最后一个位置的状态,分情况讨论
- 子序列和子数组:子序列问题的状态表示一般为[0, i]区间内所有的子序列…;而由于子数组必须连续的原因,状态表示必须是以i位置为结尾的所有子数组…
二、相关编程题
2.1 最长公共子序列
题目链接
题目描述
算法原理
编写代码
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m = text1.size();
int n = text2.size();
vector<vector<int>> dp(m+1, vector<int>(n+1));
text1 = " "+text1;
text2 = " "+text2;
for(int i = 1; i <= m; ++i)
{
for(int j = 1; j <= n; ++j)
{
if(text1[i] == text2[j])
dp[i][j] = dp[i-1][j-1]+1;
else
dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
}
}
return dp[m][n];
}
};
2.2 不相交的线
题目链接
题目描述
算法原理
编写代码
class Solution {
public:
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
vector<vector<int>> dp(m+1, vector<int>(n+1));
for(int i = 1; i <= m; ++i)
{
for(int j = 1; j <= n; ++j)
{
if(nums1[i-1] == nums2[j-1])
dp[i][j] = dp[i-1][j-1]+1;
else
dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
}
}
return dp[m][n];
}
};
2.3 不同的子序列
题目链接
题目描述
算法原理
编写代码
class Solution {
public:
int numDistinct(string s, string t) {
int m = s.size(), n = t.size();
vector<vector<double>> dp(n+1, vector<double>(m+1));
for(int j = 0; j <= m; ++j)
dp[0][j] = 1;
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
dp[i][j] = dp[i][j-1];
if(t[i-1] == s[j-1])
dp[i][j] += dp[i-1][j-1];
}
}
return dp[n][m];
}
};
2.4 通配符匹配
题目链接
题目描述
算法原理
编写代码
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size(), n = p.size();
vector<vector<bool>> dp(m+1, vector<bool>(n+1));
//初始化
dp[0][0] = true;
for(int j = 1; j <= n; ++j)
{
if(p[j-1] == '*') dp[0][j] = true;
else break;
}
//填表
for(int i = 1; i <= m; ++i)
{
for(int j = 1; j <= n; ++j)
{
if(s[i-1] == p[j-1] && dp[i-1][j-1]) dp[i][j] = true; //普通字符匹配
else //通配符匹配
{
if(p[j-1] == '?' && dp[i-1][j-1]) dp[i][j] = true;
if(p[j-1] == '*') dp[i][j] = dp[i][j-1] || dp[i-1][j];
}
}
}
return dp[m][n];
}
};
2.5 正则表达式匹配
题目链接
题目描述
算法原理
编写代码
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size(), n = p.size();
s = " "+s;
p = " "+p;
vector<vector<bool>> dp(m+1, vector<bool>(n+1));
//初始化
dp[0][0] = true;
for(int j = 2; j <= n; j+=2)
{
if(p[j] == '*')
dp[0][j-1] = dp[0][j] = true;
else
break;
}
//填表
for(int i = 1; i <= m; ++i)
{
for(int j = 1; j <= n; ++j)
{
if(p[j] == '*')
dp[i][j] = dp[i][j-2] || (p[j-1]=='.' || p[j-1]==s[i]) && dp[i-1][j];
else
dp[i][j] = (p[j] == '.' || s[i]==p[j]) && dp[i-1][j-1];
}
}
return dp[m][n];
}
};
2.6 交错字符串
题目链接
题目描述
算法原理
编写代码
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
int m = s1.size(), n = s2.size();
if(m+n!=s3.size()) return false;
s1 = " "+s1;
s2 = " "+s2;
s3 = " "+s3;
vector<vector<bool>> dp(m+1, vector<bool>(n+1));
//初始化
dp[0][0] = true;
for(int i = 1; i <= m; ++i)
if(s1[i] == s3[i]) dp[i][0] = true;
else break;
for(int j = 1; j <= n; ++j)
if(s2[j] == s3[j]) dp[0][j] = true;
else break;
//填表
for(int i = 1; i <= m; ++i)
{
for(int j = 1; j <= n; ++j)
{
dp[i][j] = (s1[i] == s3[i+j] && dp[i-1][j])
|| (s2[j] == s3[i+j] && dp[i][j-1]);
}
}
return dp[m][n];
}
};
2.7 两个字符串的最小ASCII删除和
题目链接
712. 两个字符串的最小ASCII删除和 - 力扣(LeetCode)
题目描述
算法原理
注意:
- 正难则反:删除的ASCII码最小和不好求转而求剩下的公共子序列的ASCII码最大和
- 分析状态转移方程时讨论的第四种情况其实是包含在前两种情况当中的。因为dp[i][j]包含的是区间内的所有子序列,包含有或没有i, j位置的字符。
编写代码
class Solution {
public:
int minimumDeleteSum(string s1, string s2) {
int m = s1.size(), n = s2.size();
vector<vector<int>> dp(m+1, vector<int>(n+1));
for(int i = 1; i <= m; ++i)
{
for(int j = 1; j <= n; ++j)
{
dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
if(s1[i-1] == s2[j-1])
dp[i][j] = max(dp[i][j], dp[i-1][j-1]+s1[i-1]);
}
}
int sum = 0;
for(auto e : s1) sum+=e;
for(auto e : s2) sum+=e;
return sum-dp[m][n]*2;
}
};
2.8 最长重复子数组
题目链接
题目描述
算法原理
为什么状态表示不采用[0,i]区间内所有的子数组呢?
不同于子序列,子数组要求顺序且连续,因此需要采用以i位置为结尾的状态表示才能在后续状态转移方程的推导中保证子数组连续。
编写代码
class Solution {
public:
int findLength(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
vector<vector<int>> dp(m+1, vector<int>(n+1));
int ret = 0;
for(int i = 1; i <= m; ++i)
{
for(int j = 1; j <= n; ++j)
{
if(nums1[i-1] == nums2[j-1])
dp[i][j] = dp[i-1][j-1]+1;
ret = max(ret, dp[i][j]);
}
}
return ret;
}
};