先看问题描述:有两个字符串,求解他们的最长公共子序列(LCS)。举个例子:
s1="etc",s2="ecb",最长的公共子序列为"ec",长度为2
首先声明子序列、子串的区别。子序列不连续,字串连续。比如"ab","ac"都是字符串"abc"的子序列,而"ab"是"abc"子串,但"ac"不是,因为不连续。下面给出一个命题:
命题1:假如,而是他们的一个最长公共子序列,则:
(1)若,则是的一个最长公共子序列。证明:若不然,存在一个更长的公共子序列,那么也是的公共子序列,又因为,那么他们的最长公共子序列长度一定大于的长度,矛盾。
(2)若,那么则有是的最长公共子序列。证明:首先已经是的公共子序列了,若存在更长的公共子序列,那么也是的公共子序列且长度大于,预定义矛盾。
(3)若,那么则有是的最长公共子序列。证明:同上。
根据以上命题就可以给出状态数组和状态转移方程。定义
,表示的最长公共子序列长度,状态方程如下:
下面贴出代码:
int LCS(string s1, string s2) {
int m = s1.length();
int n = s2.length();
int ** dp = new int*[m+1];
for(int i = 0;i < m+1;i++)
{
dp[i] = new int[n+1]();
}
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 if(dp[i-1][j] < dp[i][j-1])
{
dp[i][j] = dp[i][j-1];
}
else{
dp[i][j] = dp[i-1][j];
}
}
}
return dp[m][n];
}
再看如下问题:有两个字符串,求解ASCII之和最大的公共子序列。举个例子:
输入: s1 = "sea", s2 = "eat"
输出: 198
解释: ASCII之和最大公共子序列"ea",ASCII之和101 + 97 = 198。
我们仍然套用LCS的模板,先给出如下命题(证明过程同命题1):
命题1:假如,而是他们的一个ACSCII之和最大的公共子序列,和为,则:
(1)若,则是的一个ACSCII之和最大公共子序列的和。
(2)若,那么则有是的最长公共子序列。
(3)若,那么则有是的最长公共子序列。
状态数组 , 表示的ACSCII之和最大公共子序列的和,状态方程如下表示,:
代码如下:
int MCS(string s1, string s2) {
int m = s1.length();
int n = s2.length();
int ** dp = new int*[m+1];
for(int i = 0;i < m+1;i++)
{
dp[i] = new int[n+1]();
}
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] + s1[i-1];
}
else if(dp[i-1][j] < dp[i][j-1])
{
dp[i][j] = dp[i][j-1];
}
else{
dp[i][j] = dp[i-1][j];
}
}
}
return dp[m][n];
}
接下来解决两个字符串最大公共子串问题,先看问题:
给两个字符串s1 和 s2
,返回两公共的、长度最长的子串的长度。举个例子;
输入:
s1: "12321"
s2: "32147"
输出:3
解释:
长度最长的公共子串是 "321" 。
可以看到子串与子序列相比有连续性,不会发生断裂。根据以上性质,继续分析。假设是的最长的、且都以结尾的最长子序列,就是说是最大公共子串,且满足。如果,则满足以上条件最长子串长度为,如果,那么最长子串长度为。证明过程可用发证法。
根据以上分析得出状态数组 , 表示的最长且以结尾的最长公共子串的长度,状态方程如下表示:
遍历所有就能找到答案,代码如下:
int findLength(string s1, string s2) {
int m = s1.length();
int n = s2.length();
int ** dp = new int*[m+1];
for(int i = 0;i < m+1;i++)
{
dp[i] = new int[n+1]();
}
int counter = 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;
}
counter = max(counter,dp[i][j]);
}
}
return counter;
}