目录
一、最长递增子序列
原题力扣300 . - 力扣(LeetCode)
定义 dp[i] 为以第 i 个元素为结尾的最长递增子序列长度
1、O(n^2)做法
int longestIncreasingSubsequence(vector<int>& nums)
{
int n = nums.size();
vector<int> dp(n, 1);
for (int i = 1; i < n; i++)
{
for (int j = 0; j < i; j++)
{
if (nums[i] > nums[j])
{
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
return *max_element(dp.begin(), dp.end());
}
二、最长连续递增子序列
原题力扣674 . - 力扣(LeetCode)
定义 dp[i] 为以第 i 个元素为结尾的最长连续递增子序列长度
int findLengthOfLCIS(vector<int>& nums)
{
int n = nums.size();
vector<int> dp(n, 1);
for (int i = 1; i < n; i++)
{
if (nums[i] > nums[i - 1])
{
dp[i] = dp[i - 1] + 1;
}
}
return *max_element(dp.begin(), dp.end());
}
三、最长重复子数组
原题力扣718 . - 力扣(LeetCode)
1、方法一
定义 dp[i][j] :分别以 i-1,j-1 为结尾的nums1,nums2最长重复子数组的长度
int findLength(vector<int>& nums1, vector<int>& nums2)
{
vector<vector<int>> dp(nums1.size() + 1, vector<int>(nums2.size() + 1));
for (int i = 0; i <= nums1.size(); i++)
{
dp[i][0] = 0;
}
for (int j = 0; j <= nums2.size(); j++)
{
dp[0][j] = 0;
}
int ans = 0;
for (int i = 1; i <= nums1.size(); i++)
{
for (int j = 1; j <= nums2.size(); j++)
{
if (nums1[i - 1] == nums2[j - 1])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
ans = max(ans, dp[i][j]);
}
}
return ans;
}
2、方法二
定义 dp[i][j] :分别以 i,j 为结尾的nums1,nums2最长重复子数组的长度
int findLength(vector<int>& nums1, vector<int>& nums2)
{
vector<vector<int>> dp(nums1.size(), vector<int>(nums2.size()));
for (int i = 0; i < nums1.size(); i++)
{
if (nums1[i] == nums2[0])
{
dp[i][0] = 1;
}
}
for (int j = 0; j < nums2.size(); j++)
{
if (nums1[0] == nums2[j])
{
dp[0][j] = 1;
}
}
int ans = 0;
for (int i = 0; i < nums1.size(); i++)
{
for (int j = 0; j < nums2.size(); j++)
{
if (i > 0 && j > 0 && nums1[i] == nums2[j])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
ans = max(ans, dp[i][j]);
}
}
return ans;
}
3、滚动数组优化
int findLength(vector<int>& nums1, vector<int>& nums2)
{
vector<int> dp(nums2.size() + 1, 0);
int ans = 0;
for (int i = 1; i <= nums1.size(); i++)
{
for (int j = nums2.size(); j > 0; j--)
{
if (nums1[i - 1] == nums2[j - 1])
{
dp[j] = dp[j - 1] + 1;
}
else
{
dp[j] = 0; // 注意这里不相等的时候要有赋0的操作
}
ans = max(ans, dp[j]);
}
}
return ans;
}
四、最长公共子序列
原题力扣1143 . - 力扣(LeetCode)
定义 dp[i][j] :分别以 i-1,j-1 为结尾的 text1,text2 字符串的最长公共子序列长度
注意与上题比较
变形:力扣1035 . - 力扣(LeetCode) 代码一模一样
int longestCommonSubsequence(string text1, string text2)
{
vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1));
for (int i = 1; i <= text1.size(); i++)
{
for (int j = 1; j <= text2.size(); j++)
{
if (text1[i - 1] == text2[j - 1])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else
{
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[text1.size()][text2.size()];
}
变形:力扣392 . - 力扣(LeetCode)
bool isSubsequence(string s, string t)
{
vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1, 0));
for (int i = 1; i <= s.size(); i++)
{
for (int j = 1; j <= t.size(); j++)
{
if (s[i - 1] == t[j - 1])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else
{
dp[i][j] = dp[i][j - 1]; // 只能从 t 中删除元素
}
}
}
return dp[s.size()][t.size()] == s.size();
}
五、最大子数组和
原题力扣53 . - 力扣(LeetCode)
定义 dp[i]:以 nums[i] 为结尾的最大子数组和
int maxSubArray(vector<int>& nums)
{
int n = nums.size();
vector<int> dp(n, 0);
dp[0] = nums[0];
for (int i = 1; i < n; i++)
{
dp[i] = max(dp[i - 1] + nums[i], nums[i]);
}
return *max_element(dp.begin(), dp.end());
}
六、不同的子序列
原题力扣115 . - 力扣(LeetCode)
定义 dp[i][j]:以 i-1 为结尾的 s 中含有以 j-1 为结尾的 t 的个数
int numDistinct(string s, string t)
{
vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1, 0));
// 注意初始化,s 中删除任意元素后变成空串的个数只有 1 个
for (int i = 0; i <= s.size(); i++)
{
dp[i][0] = 1;
}
for (int i = 1; i <= s.size(); i++)
{
for (int j = 1; j <= t.size(); j++)
{
if (s[i - 1] == t[j - 1])
{
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
}
else
{
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[s.size()][t.size()];
}
七、两个字符串的删除操作
原题力扣583 . - 力扣(LeetCode)
定义 dp[i][j]:以 i-1 为结尾的 word1,以 j-1 为结尾的 word2 的字符串,删除字符使它们相同的最小操作次数
int minDistance(string word1, string word2)
{
vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
for (int i = 0; i <= word1.size(); i++)
{
dp[i][0] = i;
}
for (int j = 0; j <= word2.size(); j++)
{
dp[0][j] = j;
}
for (int i = 1; i <= word1.size(); i++)
{
for (int j = 1; j <= word2.size(); j++)
{
if (word1[i - 1] == word2[j - 1])
{
dp[i][j] = dp[i - 1][j - 1];
}
else
{
// 三种情况:删除word1[i-1]、删除word2[j-1]、同时删除word1和word2
dp[i][j] = min(dp[i - 1][j] + 1, min(dp[i][j - 1] + 1, dp[i - 1][j - 1] + 2));
}
}
}
return dp[word1.size()][word2.size()];
}
八、编辑距离
原题力扣72 . - 力扣(LeetCode)
int minDistance(string word1, string word2)
{
vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
for (int i = 0; i <= word1.size(); i++)
{
dp[i][0] = i;
}
for (int j = 0; j <= word2.size(); j++)
{
dp[0][j] = j;
}
for (int i = 1; i <= word1.size(); i++)
{
for (int j = 1; j <= word2.size(); j++)
{
if (word1[i - 1] == word2[j - 1])
{
dp[i][j] = dp[i - 1][j - 1];
}
else
{
// 增删操作相同、替换dp[i - 1][j - 1] + 1
dp[i][j] = min(dp[i - 1][j] + 1, min(dp[i][j - 1] + 1, dp[i - 1][j - 1] + 1));
}
}
}
return dp[word1.size()][word2.size()];
}
九、回文子串
原题力扣647 . - 力扣(LeetCode)
定义 dp[i][j]:[i...j]的子串中回文串的数量
int countSubstrings(string s)
{
vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));
int ans = 0;
// 注意遍历顺序,由递推公式知,从下到上,从左到右
for (int i = s.size() - 1; i >= 0; i--)
{
for (int j = i; j < s.size(); j++)
{
if (s[i] == s[j])
{
if (j - i <= 1)
{
dp[i][j] = true;
ans++;
}
else if (dp[i + 1][j - 1] == true)
{
dp[i][j] = true;
ans++;
}
}
}
}
return ans;
}
十、最长回文子序列
原题力扣516 . - 力扣(LeetCode)
定义 dp[i][j]:[i...j]范围的子串的最长回文子序列的长度
int longestPalindromeSubseq(string s)
{
vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
for (int i = 0; i < s.size(); i++)
{
dp[i][i] = 1;
}
for (int i = s.size() - 1; i >= 0; i--)
{
for (int j = i + 1; j < s.size(); j++)
{
if (s[i] == s[j])
{
dp[i][j] = dp[i + 1][j - 1] + 2;
}
else
{
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][s.size() - 1];
}