动态规划(Dynamic Programming, DP)在字符串数组相关的算法题中应用广泛,尤其是在解决子序列、子串、编辑距离、匹配等问题时。动态规划的核心思想是将问题分解为子问题,并通过存储子问题的解来避免重复计算,从而提高效率。
Given two strings text1
and text2
, return the length of their longest common subsequence. If there is no common subsequence, return 0.
A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.
For example, "ace"
is a subsequence of "abcde"
.
A common subsequence of two strings is a subsequence that is common to both strings.
Example 1:
Input: text1 = "abcde", text2 = "ace"
Output: 3
Explanation: The longest common subsequence is "ace" and its length is 3.
Example 2:
Input: text1 = "abc", text2 = "abc"
Output: 3
Explanation: The longest common subsequence is "abc" and its length is 3.
Example 3:
Input: text1 = "abc", text2 = "def"
Output: 0
Explanation: There is no such common subsequence, so the result is 0.
给定两个字符串 s1
和 s2
,找到它们的最长公共子序列的长度。子序列是指从原字符串中删除一些字符(可以不连续)后得到的新字符串。
dp[i][j]
表示 s1
的前 i
个字符和 s2
的前 j
个字符的最长公共子序列长度。s1[i-1] == s2[j-1]
,则 dp[i][j] = dp[i-1][j-1] + 1
。dp[i][j] = max(dp[i-1][j], dp[i][j-1])
。dp[0][j] = 0
和 dp[i][0] = 0
,表示空字符串的 LCS 长度为 0。dp[m][n]
,其中 m
和 n
分别是 s1
和 s2
的长度。int longestCommonSubsequence(string s1, string s2) {
int m = s1.length(), n = s2.length();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (s1[i - 1] == s2[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[m][n];
}
Given two strings str1
and str2
, return the shortest string that has both str1
and str2
as subsequences. If there are multiple valid strings, return any of them.
A string s
is a subsequence of string t
if deleting some number of characters from t
(possibly 0) results in the string s
.
Example 1:
Input: str1 = "abac", str2 = "cab"
Output: "cabac"
Explanation:
str1 = "abac" is a subsequence of "cabac" because we can delete the first "c".
str2 = "cab" is a subsequence of "cabac" because we can delete the last "ac".
The answer provided is the shortest such string that satisfies these properties.
Example 2:
Input: str1 = "aaaaaaaa", str2 = "aaaaaaaa"
Output: "aaaaaaaa"
这个问题可以转化为求两个字符串的最长公共子序列(LCS),然后通过 LCS 构造最短的公共超序列。
构造最短公共超序列
i
和 j
分别遍历 str1
和 str2
。str1[i] == str2[j]
,则将当前字符添加到结果中,并移动两个指针。str1[i]
或 str2[j]
中较小的字符添加到结果中,并移动对应的指针。str1
或 str2
有剩余字符,将它们全部添加到结果中。string shortestCommonSupersequence(string str1, string str2) {
int m = str1.length(), n = str2.length();
// 动态规划求 LCS
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (str1[i - 1] == str2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
// 构造最短公共超序列
string result;
int i = m, j = n;
while (i > 0 && j > 0) {
if (str1[i - 1] == str2[j - 1]) {
result.push_back(str1[i - 1]);
i--;
j--;
} else if (dp[i - 1][j] > dp[i][j - 1]) {
result.push_back(str1[i - 1]);
i--;
} else {
result.push_back(str2[j - 1]);
j--;
}
}
// 处理剩余字符
while (i > 0) {
result.push_back(str1[i - 1]);
i--;
}
while (j > 0) {
result.push_back(str2[j - 1]);
j--;
}
// 反转结果
reverse(result.begin(), result.end());
return result;
}