HDU1159 Common Subsequence(最长公共子序列LCS)

来源:互联网 发布:php框架排行 yii 编辑:程序博客网 时间:2024/06/14 17:30

题目:

Common Subsequence

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 36328    Accepted Submission(s): 16630


Problem Description
A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = <x1, x2, ..., xm> another sequence Z = <z1, z2, ..., zk> is a subsequence of X if there exists a strictly increasing sequence <i1, i2, ..., ik> of indices of X such that for all j = 1,2,...,k, xij = zj. For example, Z = <a, b, f, c> is a subsequence of X = <a, b, c, f, b, c> with index sequence <1, 2, 4, 6>. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y. 
The program input is from a text file. Each data set in the file contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct. For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line. 
 

Sample Input
abcfbc abfcabprogramming contest abcd mnp
 

Sample Output
420
 

Source
Southeastern Europe 2003
 

Recommend
Ignatius
 

Statistic | Submit | Discuss | Note
思路:

给了两个字符串,求最长公共子序列,最长公共子序列的递推式为


求两字符序列的最长公共字符子序列

问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij=yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。

考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bm-1”,并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。不难证明有以下性质:

(1) 如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…,bn-2”的一个最长公共子序列;

(2) 如果am-1!=bn-1,则若zk-1!=am-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列;

(3) 如果am-1!=bn-1,则若zk-1!=bn-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列。

这样,在找A和B的公共子序列时,如有am-1=bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”和“b0,b1,…,bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。


上面的分析过程是不是有点看不懂,我在这里用大白话简单地说一下,先给出三个序列,A、B序列分别代表第一和第二个字符串,字符串Z代表它们两个的最长公共子序列,分三种情况:

①:当A序列和B序列的最后一项相等时,则Z的最后一项和A与B的最后一项相等,则它们的最长公共子序列(以下文用LCS来表示)z的第一项到z的的倒数第二项,是A和B的从它们的第一项到它们的倒数第二项的一个LCS。

②:当A的最后一项不等于B的最后一项Z的最后一项不等于A的最后一项,表示从Z的第一项起到Z的最后一项从A的第一项起到A的倒数第二项和从B的第一项起到B的倒数第一项的一个LCS.

③:当A的最后一项不等于B的最后一项Z的最后一项不等于B的最后一项,表示从Z的第一项起到Z的最后一项项从A的第一项起到A的倒数第一项和从B的第一项起到B的倒数第二项的一个LCS.

按照上面的结论,我们可以从后面往前递推,最后得到递推式(参考上面的图)

求解:

引进一个二维数组c[][],用c[i][j]记录X[i]与Y[j] 的LCS 的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定搜索的方向。
我们是自底向上进行递推计算,那么在计算c[i,j]之前,c[i-1][j-1],c[i-1][j]与c[i][j-1]均已计算出来。此时我们根据X[i] = Y[j]还是X[i] != Y[j],就可以计算出c[i][j]。


代码:

#include <stdio.h>#include <string.h>#include <string>#include <iostream>#include <stack>#include <queue>#include <vector>#include <algorithm>#define mem(a,b) memset(a,b,sizeof(a))using namespace std;char a[2000],b[2000];int dp[2000][2000];//dp[i][j]表示匹配到a字符串的第i个字符和b字符串的第j个字符时的最大匹配数int main(){    while(~scanf("%s%s",a,b))    {        int lena=strlen(a);        int lenb=strlen(b);//计算长度        for(int i=0; i<=lena; i++)            dp[i][0]=0;//初始化边界        for(int i=0; i<=lenb; i++)            dp[0][i]=0;        for(int i=1; i<=lena; i++)        {             for(int j=1; j<=lenb; 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]);             }        }        printf("%d\n",dp[lena][lenb]);    }    return 0;}



0 0
原创粉丝点击