题目描述
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = “abcde”, text2 = “ace”
输出:3
解释:最长公共子序列是 “ace” ,它的长度为 3 。
示例 2:
输入:text1 = “abc”, text2 = “abc”
输出:3
解释:最长公共子序列是 “abc” ,它的长度为 3 。
示例 3:
输入:text1 = “abc”, text2 = “def”
输出:0
解释:两个字符串没有公共子序列,返回 0 。
使用动态规划,dp[i][j]表示在text1的前 i 个字符与text2的前 j 个字符之间最大公共子序列的长度。
- 如果 text1[i] == text2[j] ,那么可以由dp[i-1][j-1]转化而来
- 如果二者不相等,就要看text1[0 ~ i-1] 与 text2[0 ~ j] 以及 text1[0 ~ i ] 与 text2[0 ~ j-1] 这两者哪个公共子序列最大。
- 边界条件,如果 i== 0 那么 dp[i][j]=0, 如果j==0,那么dp[i][j]=0,可以在初始化的时候将数组边界条件处理掉。
- 最后输出的是dp[n][m]
为了减少判断条件,循环下标从1开始。
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int n=text1.size(),m=text2.size();
vector<vector<int>> dp(n+1,vector<int>(m+1,0));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;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[n][m];
}
};
变种:NC127 最长公共子串
描述
给定两个字符串str1和str2,输出两个字符串的最长公共子串
题目保证str1和str2的最长公共子串存在且唯一。
示例1
输入:“1AB2345CD”,“12345EF”
返回值: “2345”
注意:子串是连续的,子序列是不连续的,因此动态规划方程也发生了一定的变化。
dp[i][j]表示字符串str1中第i个字符和str2种第j个字符为最长公共子串的最后一个元素。如果要求dp[i][j],也就是str1的第i个字符和str2的第j个字符为最后一个元素所构成的最长公共子串。
因此如果str1[i]!=str2[j]那么说明无法构成公共子串了,因此此时的值是0。
由于题目中要求返回最长公共子串,需要在计算过程中记录最长公共子串的结束位置以及子串的长度。
class Solution {
public:
/**
* longest common substring
* @param str1 string字符串 the string
* @param str2 string字符串 the string
* @return string字符串
*/
string LCS(string str1, string str2) {
// write code here
int n = str1.size(),m=str2.size();
vector<vector<int>> dp(n+1,vector<int>(m+1,0));
int len=0,pos=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(str1[i-1]==str2[j-1]) dp[i][j]=dp[i-1][j-1]+1;
if(dp[i][j]>len)
{
len=max(len,dp[i][j]);
pos=i-1;
}
}
}
if(len==0) return "-1";
return str1.substr(pos-len+1,len);
}
};