标签: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