问题描述
给出两个字符串,求出这样的一个最长的公共子序列的长度:子序列中的每个字符都能在两个原串中找到,而且每个字符的先后顺序和原串中的顺序一致。
样例输入
abcfbc abfcab
programming contest
abcd mnp
样例输出
4
2
0
算法思想
输入两个串s1,s2,设MaxLen(I,j)表示:
s1左边的第i个字符形成的子串,与s2左边的j个字符形成的子串的最长公共子序列的长度(i,j从0开始算)
MaxLen(i,j)就是本题的状态。
假定len1=strlen(s1),len2=strlen(s2)
那么题目就是要求MaxLen(len1,len2)
显然:
MaxLen(n,0)=0(n=0…len1)
MaxLen(0,n)=0(n=0…len2)
递推公式:
if(s1[i-1]==s2[j-1]) //s1的最左边字符时s1[0]
MaxLen(i,j)= MaxLen(i-1,j-1)+1
(s1的左边形成的i个字符和s2的左边j个字符所形成的最长公共子序列的长度等于s1左边的i-1个字符和s2左边的j-1个字符所形成的最长公共子序列的长度+1)
else
MaxLen(i,j)=Max(MaxLen(i,j-1), MaxLen(i-1,j))
(s1左边的i个字符和s2的左边j-1个字符,s1左边的i-1个字符和s2左边的j个字符所形成的公共子序列的长度的最大值)
时间复杂度O(mn)m,n是两个子串长度
s1[i-1]!=s2[j-1],maxlen(s1,s2)不会比maxlen(s1,s2j-1)和maxlen(s1i-1,s2)两者汇总的任何一个小,也不会比两者都大。
前者,显然成立。
对于后者,反证法,假设maxlen(s1,s2)比maxlen(s1,s2j-1)大,它们的差别在于前者比后者多了一个字符,多出来的字符时s2[j-1],说明s2[j-1]要起作用,说明它是属于他们的最长公共子序列,maxlen(s1,s2)比maxlen(s1i-1,s2)大,同理,s1[i-1]起作用,它必然是s1和s2所形成的最长公共子序列的字符,所以s1[i-1]和s2[j-1]均是s1和s2最长公共子序列的最后一个字符。与前提矛盾,说明前面的假设不成立,
则s1[i-1]!=s2[j-1],maxlen(s1,s2)不会比maxlen(s1,s2j-1)和maxlen(s1i-1,s2)两者汇总的任何一个小,也不会比两者都大。
程序代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXSIZE 100
char a[MAXSIZE];
char b[MAXSIZE];
int maxLen[MAXSIZE][MAXSIZE];
int commonStr(char a [ ],char b [ ]){
//计算两个字符串的长度
int len_a=strlen(a);
int len_b=strlen(b);
int i,j;
//设置边界条件
for(i=0;i<=len_b;i++){
maxLen[len_a][0]=0;
}
for(i=0;i<=len_a;i++){
maxLen[0][len_b]=0;
}
for(i=0;i<len_a;i++){
for(j=0;j<len_b;j++){
//当a[i]等于b[j]
//s1的左边形成的i个字符和s2的左边j个字符所形成的最长公共子序列的长度等于
//s1左边的i-1个字符和s2左边的j-1个字符所形成的最长公共子序列的长度+1
if(a[i]==b[j]){
maxLen[i+1][j+1]=maxLen[i][j]+1;
}else{
//当a[i]不等于b[j]时
//s1左边的i个字符和s2的左边j-1个字符,s1左边的i-1个字符和s2左边的j个字符所形成的公共子序列的长度的最大值
maxLen[i+1][j+1]=max(maxLen[i][j+1],maxLen[i+1][j]);
}
}
}
return maxLen[len_a][len_b];
}
int main(){
int *res=new int[MAXSIZE];
int num=0;
while(cin>>a>>b){
res[num++]=commonStr(a,b);
}
for(int i=0;i<num;i++){
cout<<res[i]<<endl;
}
return 0;
}