标签:div 前缀 round 解决 第一个字符 bsp cab ack cpp
最长公共子序列(LCS) ----以51nod1006为例
记:
xi x序列的前i个字符(前缀)
yj y序列的前j个字符(前缀)
假设z是LCS(x,y)
若xm=yn(最后一个字符相同),则不难用反证法证明:
该字符必须是x与y的任一最长公共子序列z(设长度为k)
的最后一个字符,即zk=xm=yn 且显然有zk-1∈LCS(xm-1,yn-1)
即z的前缀zk-1是xm-1与yn-1的最长公共子序列。此时,问题转归于求
xm-1与yn-1的LCS(x,y)的长度即等于LCS(xm-1,yn-1)的长度+1;
若xm≠yn,则以不难用反证法证明:要么z∈LCS(xm-1,y),要么z∈LCS(x,yn-1).
由于zk≠xm,zk≠yn其中至少有一个必成立,若zk≠xm则有z∈LCS(xm-1,y)
,类似的,若zk≠yn则有z∈LCS(x,yn-1),此时,问题划归于求xm-1与y的LCS
及x与yn-1的LCS。LCS(x,y)的长度为:max(LCS(xm-1,y),LCS(想,yn-1))
的长度
由与上述当xm≠yn的情况时,求LCS(xm-1,y)的长度与LCS(x,yn-1)的长度
这两个问题是不相互独立:
两者都需要求LCS(xm-1,yn-1)的长度。另外两个序列的LCS包含了两个序列
前缀的LCS,故问题具有最优子结构考虑用动态规划
也就是说,解决这个问题,需求:
1:LCS(xm-1,yn-1)+1;
2: LCS(xm-1,y),LCS(x,yn-1)
3:max(LCS(xm-1,y),LCS(x,yn-1))
我们定义c[i,j]表示xi和yi的LCS的长度
如果i=0或j=0,即一个序列长度为0,那么LCS为0;
根据LCS问题的最优子结构性质,可得到如下公式:
0 若i=0或j=0;
c[i,j]=c[i-1,j-1]+1 若i,j>0&&xi=yi
max(c[i-1,j],c[i,j-1]) 若i,j>0&&xi≠yi
#include <stdio.h>
#include <iostream>
#include <string.h>
const int maxn=1005;
int dp[maxn][maxn]={0};
using namespace std;
int main(){
char a[maxn],b[maxn],lcs[maxn];
int i,j;
scanf("%s %s",a,b);
int lena=strlen(a),lenb=strlen(b);
for(i=1;i<=lena;i++){ //i的位置固定不动,遍历j,找到i对应的字符串中与当前位置相同的元素 不好意思啦,上下写反了
for(j=1;j<=lenb;j++){ //遍历i,找到j对应的字符串中与当前位置相同的元素,j的位置固定不动
if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
//第一个字符串先往后移,找到与第二个字符串当前位置匹配的元素,第二个字符串先往后移
//对这两种情况进行比较,得到最大值,存入用来存储子串的数组中
//c[i,j]=Max((c[i,j-1]),(c[i-1,j]))
}
}
i=lena,j=lenb; //以第一题为例,abcicba 与 abdkscab ,
//i=7,j=8;
int len=dp[lena][lenb]; //比较结束时LCS的长度,本例为4
lcs[len]=‘\0‘; //lcs数组存储从0,1,2,3,‘\0‘
while(dp[i][j]){ //dp[7][8]=4>0
//若对应的位置不一样就根据数组通过字符串找到子串中的元素,然后赋值给要输出的字符串
if(dp[i][j]==dp[i-1][j]) i--; //dp[7][8]=dp[6][8] i=6;
else if(dp[i][j]==dp[i][j-1]) j--; //dp[7][8]=dp[7][7] j=7;
else lcs[--len]=a[i-1],i--,j--;
//不是b[i-1] 当i,j对应的位置一样时,然后把需要的值赋给要输出的字符串,
}
printf("%s\n",lcs);
return 0;
}
标签:div 前缀 round 解决 第一个字符 bsp cab ack cpp
原文地址:http://www.cnblogs.com/z-712/p/7290173.html