codevs 1862 最长公共子序列(求最长公共子序列长度并统计最长公共子序列的个数)

题目描述 Description

字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列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的一个子序列。

对给定的两个字符序列,求出他们最长的公共子序列长度,以及最长公共子序列个数。

输入描述 Input Description

第1行为第1个字符序列,都是大写字母组成,以”.”结束。长度小于5000。

第2行为第2个字符序列,都是大写字母组成,以”.”结束,长度小于5000。

输出描述 Output Description

第1行输出上述两个最长公共子序列的长度。

第2行输出所有可能出现的最长公共子序列个数,答案可能很大,只要将答案对100,000,000求余即可。

样例输入 Sample Input

ABCBDAB.
BACBBD.

样例输出 Sample Output

4
7

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5
 6 #define maxn 5010
 7 #define MOD 100000000
 8 char A[maxn],B[maxn],cur;
 9 int f[2][maxn], g[2][maxn];
10
11 int main()
12 {
13     scanf("%s%s", A + 1, B + 1);
14     int na = strlen(A + 1), nb = strlen(B + 1);
15     A[na--] = ‘\0‘; B[nb--] = ‘\0‘;
16
17     cur=0;
18     for(int i = 1; i <= nb; i++) g[0][i] = 1;
19     g[0][0] = g[1][0] = 1;
20
21     for(int i = 1; i <= na; i++)
22     {
23         cur ^= 1;
24         for(int j = 1; j <= nb; j++)
25         {
26             if(A[i] == B[j]) f[cur][j] =f[cur^1][j-1] + 1;
27             else f[cur][j] = max(f[cur^1][j], f[cur][j-1]);
28
29             g[cur][j] = 0;
30             if(f[cur][j] == f[cur^1][j]) g[cur][j] += g[cur^1][j];
31             if(f[cur][j] == f[cur][j-1]) g[cur][j] += g[cur][j-1];
32             if(f[cur][j] == f[cur^1][j] && f[cur][j] == f[cur][j-1] && f[cur^1][j-1] == f[cur][j])
33                 g[cur][j] -= g[cur^1][j-1];
34             if(A[i] == B[j] && f[cur][j] == f[cur^1][j-1] + 1) g[cur][j] += g[cur^1][j-1];
35             if(g[cur][j] > MOD) g[cur][j] %= MOD;
36             if(g[cur][j] < 0) g[cur][j] = (g[cur][j] % MOD) + MOD;
37         }
38     }
39     printf("%d\n%d\n", f[cur][nb], g[cur][nb]);
40     return 0;
41 }

参考1:http://blog.csdn.net/moep0/article/details/52760974

参考2:http://blog.csdn.net/litble/article/details/67640655

算法分析:

第一个问题可以参考最长公共子序列原题的题解。

第二个问题可以阅读参考1参考2的分析和代码自己理解。

这里摘抄参考1里面的两段代码留存:

代码一:使用了滚动数组。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cmath>
 5 #include <stack>
 6 #include <vector>
 7 #include <queue>
 8 #include <cstring>
 9 #include <string>
10 #include <map>
11 #include <set>
12 using namespace std;
13
14 const int BufferSize = 1 << 16;
15 char buffer[BufferSize], *Head, *Tail;
16 inline char Getchar() {
17     if(Head == Tail) {
18         int l = fread(buffer, 1, BufferSize, stdin);
19         Tail = (Head = buffer) + l;
20     }
21     return *Head++;
22 }
23 int read() {
24     int x = 0, f = 1; char c = Getchar();
25     while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = Getchar(); }
26     while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = Getchar(); }
27     return x * f;
28 }
29
30 #define maxn 5010
31 #define MOD 100000000
32 char A[maxn], B[maxn], cur;
33 int f[2][maxn], g[2][maxn];
34
35 int main() {
36     scanf("%s%s", A + 1, B + 1);
37     int na = strlen(A + 1), nb = strlen(B + 1);
38     A[na--] = ‘\0‘; B[nb--] = ‘\0‘;
39
40     for(int i = 1; i <= nb; i++) g[0][i] = 1; g[0][0] = g[1][0] = 1;
41     for(int i = 1; i <= na; i++) {
42         cur ^= 1;
43         for(int j = 1; j <= nb; j++) {
44             f[cur][j] = max(f[cur^1][j], f[cur][j-1]);
45             if(A[i] == B[j]) f[cur][j] = max(f[cur][j], f[cur^1][j-1] + 1);
46             g[cur][j] = 0;
47             if(f[cur][j] == f[cur^1][j]) g[cur][j] += g[cur^1][j];
48             if(f[cur][j] == f[cur][j-1]) g[cur][j] += g[cur][j-1];
49             if(f[cur][j] == f[cur^1][j] && f[cur][j] == f[cur][j-1] && f[cur^1][j-1] == f[cur][j]) g[cur][j] -= g[cur^1][j-1];
50             if(A[i] == B[j] && f[cur][j] == f[cur^1][j-1] + 1) g[cur][j] += g[cur^1][j-1];
51             if(g[cur][j] > MOD) g[cur][j] %= MOD;
52             if(g[cur][j] < 0) g[cur][j] = (g[cur][j] % MOD) + MOD;
53         }
54     }
55
56     printf("%d\n%d\n", f[cur][nb], g[cur][nb]);
57
58     return 0;
59 }

代码二:不使用滚动数组,假如测试数据较小,可以AC。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<cstdio>
 6 #define mod 100000000
 7 using namespace std;
 8 string a,b;
 9 int f[5005][5005],g[5005][5005];
10 int main(){
11     cin>>a>>b;
12     int l1=a.length()-1,l2=b.length()-1;
13     a=‘ ‘+a,b=‘ ‘+b;
14     for(int i=0;i<=l1;i++)g[i][0]=1;
15     for(int i=0;i<=l2;i++)g[0][i]=1;
16     for(int i=1;i<=l1;i++)
17       for(int j=1;j<=l2;j++)
18         {
19               if(a[i]==b[j])
20               {
21                   f[i][j]=f[i-1][j-1]+1;
22                 g[i][j]=g[i-1][j-1];
23                 if(f[i][j]==f[i][j-1])g[i][j]=(g[i][j]+g[i][j-1])%mod;
24                 if(f[i][j]==f[i-1][j])g[i][j]=(g[i][j]+g[i-1][j])%mod;
25             }
26               else
27               {
28                   f[i][j]=max(f[i-1][j],f[i][j-1]);
29                   if(f[i][j]==f[i-1][j])g[i][j]=(g[i][j]+g[i-1][j])%mod;
30                 if(f[i][j]==f[i][j-1])g[i][j]=(g[i][j]+g[i][j-1])%mod;
31                 if(f[i][j]==f[i-1][j-1])g[i][j]-=g[i-1][j-1],g[i][j]=(g[i][j]+mod)%mod;
32             }
33         }
34     cout<<f[l1][l2]<<endl<<g[l1][l2]%mod;
35     return 0;
36 }

时间: 2024-11-05 10:52:50

codevs 1862 最长公共子序列(求最长公共子序列长度并统计最长公共子序列的个数)的相关文章

两组字符串中的最长公共子串(可包含多个长度相同的最长公共子串)

#include <stdio.h>#include <string.h>main(){int i,j,k,n,h,m=0,count=0,count1=0,count2=0,count3=0;char str1[100], str2[100];int str3[100]; printf("str1:"); gets(str1); printf("str2:"); gets(str2); for(h=0;str1[h]!='\0';h++){

最长公共子序列(LCS)问题 Longest Common Subsequence 与最长公告字串 longest common substr

问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列.令给定的字符序列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

poj 1226 hdu 1238 Substrings 求若干字符串正串及反串的最长公共子串 2002亚洲赛天津预选题

题目:http://poj.org/problem?id=1226 http://acm.hdu.edu.cn/showproblem.php?pid=1238 其实用hash+lcp可能也可以,甚至可能写起来更快,不过我没试,我最近在练习后缀数组,所以来练手 后缀数组的典型用法之一----------------后缀数组+lcp+二分 思路:1.首先将所有的字符串每读取一个,就将其反转,作为一组,假设其下标为i到j,那么cnt[i]到cnt[j]都标记为一个数字(这个数字意思是第几个读入的字符

POJ - 3415 Common Substrings(后缀数组求长度不小于 k 的公共子串的个数+单调栈优化)

Description A substring of a string T is defined as: T( i, k)= TiTi+1... Ti+k-1, 1≤ i≤ i+k-1≤| T|. Given two strings A, B and one integer K, we define S, a set of triples (i, j, k): S = {( i, j, k) | k≥ K, A( i, k)= B( j, k)}. You are to give the val

POJ 3294 后缀数组:求不小于k个字符串中的最长子串

思路:先把所有的串连接成一个串,串写串之前用没出现过的字符隔开,然后求后缀:对height数组分组二分求得最长的公共前缀,公共前缀所在的串一定要是不同的,不然就不是所有串的公共前缀了,然后记下下标和长度即可. 刚开始理解错题意,然后不知道怎么写,然后看别人题解也不知道怎么意思,后面看了好久才知道题目意思理解错了. 时间四千多ms,别人才一百多ms,不知道别人怎么做的-- #include<iostream> #include<cstdio> #include<cstring&

POJ 3415 Common Substrings (求长度不小于k的公共子串的个数)

Common Substrings Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 10002   Accepted: 3302 Description A substring of a string T is defined as: T(i, k)=TiTi+1...Ti+k-1, 1≤i≤i+k-1≤|T|. Given two strings A, B and one integer K, we define S,

数据结构——算法之(032)(求两个串中的第一个最长子串)

[申明:本文仅限于自我归纳总结和相互交流,有纰漏还望各位指出. 联系邮箱:[email protected]] 题目: 求两个串中的第一个最长子串(神州数码曾经试题).如"abractyeyt","dgdsaeactyey"的最大子串为"actyey". 题目分析: 1.这里仅仅是实现了简单的字符串算法(最大支持字符串长度64),主要是展示算法思想 2.思路是把2个字符串每一个字符的匹配关系,映射到一张二维数组表中,匹配写1,非匹配写0 算法实现

(hdu step 1.3.6)Wooden Sticks(求长度和重量都严格递增的数列的个数)

题目: Wooden Sticks Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 2374 Accepted Submission(s): 921   Problem Description There is a pile of n wooden sticks. The length and weight of each stick are

POJ 3415 Common Substrings(长度不小于k 的公共子串的个数--后缀数组+单调栈优化)

题意:给定两个字符串A 和B,求长度不小于k 的公共子串的个数(可以相同). 样例1: A="xx",B="xx",k=1,长度不小于k 的公共子串的个数是5. 样例2: A ="aababaa",B ="abaabaa",k=2,长度不小于k 的公共子串的个数是22. 思路: 如果i后缀与j后缀的LCP长度为L, 在L不小于K的情况下, 它对答案的贡献为L - K + 1. 于是我们可以将两个串连起来, 中间加个奇葩的分隔符