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--] = ''; B[nb--] = '';
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
%d
", 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--] = ''; B[nb--] = '';
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
%d
", f[cur][nb], g[cur][nb]);
57      
58     return 0;
59 }
View Code

代码二:不使用滚动数组,假如测试数据较小,可以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 }
View Code
原文地址:https://www.cnblogs.com/huashanqingzhu/p/7435750.html