给定序列的子序列是给定序列,其中遗漏了一些元素(可能没有)。给定序列X = <x1,x2,…,xm>,如果存在严格递增的序列<i1,i2,…,则另一个序列Z = <z1,z2,…,zk>是X的子序列。 X的索引的,,>>使得对于所有j = 1,2,…,k,xij= zj。例如,Z = <a,b,f,c>是X = <a,b,c,f,b,c>的子序列,索引序列为<1,2,4,6>。给定两个序列X和Y,问题是找到X和Y的最大长度公共子序列的长度。
输入值
程序输入来自std输入。输入中的每个数据集都包含两个表示给定序列的字符串。序列由任意数量的空格分隔。输入数据正确。
输出量
对于每组数据,程序将从单独的行的开始在标准输出上打印最大长度的公共子序列的长度。
样本输入
abcfbc abfcab
programming contest
abcd mnp
样本输出
4
2
0
思路分析
输入两个字符串a,b。
设result[i][j]表示:a的左边i个字符形成的子串,与b左边的j个字符形成的子串的最长公共子序列的长度(i,j从0开始算)
由上面的定义可知:
- result[i][0]=0(0=<i<=a.len)
- result[0][j]=0(0=<j<=b.len)
因为1、2代表两个字符串中至少有一个字符串是空串,空串与任何字符的最长公共子序列都为空串,即最长公共子序列的长度为0,其在result中表现的规律为第0行和第0列的元素都为0.
递推式 - result[i+1][j+1]=result[i][j]+1; 当a[i]==b[j]时
- result[i+1][j+1]=max(result[i+1][j],result[i][j+1]);当a[i]!=b[j]时
因为result[i][j]代表a字符串的前i个字符(字符串数组中的下标为i-1)与b字符串的前j个字符所能构成的最长公共子序列的长度,因为a[i]==b[j],(字符串a的第i+1个字符与字符串b的第j+1字符相等)则其长度比没有当前字符的最长公共子序列的长度大1.
因为result[i+1][j+1]所包含的字符比result[i+1][j]或result[i][j+1]都多,所以它构成的最长公共子序列的长度不会比这两者中的任何一个小,假设result[i+1][j+1]构成的最长公共子序列的长度比result[i+1][j]和result[i][j+1]都大,因为result[i+1][j+1]比result[i+1][j]大,且因为两者只差b字符串的b[j]字符。所以可证明b字符串的的b[j]字符是公共子序列中的元素且是公共子序列中的最后一个元素。同理可得如果result[i+1][j+1]比result[i][j+1]大则说明a字符串的a[i]字符是公共子序列中的元素且是公共子序列中的最后一个元素。因为a[i]!=b[j]所以假设矛盾,即result[i+1][j+1]构成的最长公共子序列的长度不会比result[i+1][j]或result[i][j+1]都大。
综合result[i+1][j+1]最长公共子序列的长度不会比result[i+1][j]和result[i][j+1]的任何一个小,且构成的最长公共子序列的长度不会比result[i+1][j]或result[i][j+1]都大。所以只能等于他们的最大值。
代码
#include <iostream>
int result[10000][10000];//全局变量默认初始化为0
using namespace std;
int main()
{
string a,b;//注意点:因为全局变量已经初始化所有的元素为0了,所以无需重复初始化0行0列元素为0
while(cin>>a>>b){//注意有多组测试数据
for(int i=0;i<a.length();i++){
for(int j=0;j<b.length();j++){
if(a[i]==b[j]){//当前的字符相等则最长公共子序列长度等于
//两个字符串都除去当前字符的最长公共子序列长度加1
result[i+1][j+1]=result[i][j]+1;
}else{//当前的字符不相等,则两个字符串中当前的字符至少有一个在此处无贡献
//则最长公共子序列长度等于丢弃两个字符串中一个字符串当前字符所能构成的最长公共子序列长度的最大值
result[i+1][j+1]=max(result[i+1][j],result[i][j+1]);
}
}
}
cout<<result[a.length()][b.length()]<<endl;
}
return 0;
}
注意点
- 一次运行要输入多组测试数据。
- 注意字符串的下标与result数组下标的转换(字符串下标0已经是第1个字符了)。