【动态规划】【NOIP模拟10-21】最长公共子序列
Description
对于一个序列𝑎[1], 𝑎[2], … , 𝑎[𝑛],其子序列为一序列𝑝[1], 𝑝[2], … , 𝑝[𝑚],满 足1 ≤ 𝑝[1] < 𝑝[2] < ⋯ < 𝑝[𝑚] ≤ 𝑛, 1 ≤ 𝑚 ≤ 𝑛。𝑎的两个子序列𝑥, 𝑦是不同的,当 且其长度不同,或存在一个位置𝑖,满足𝑥[𝑖] ≠ 𝑦[𝑖]。 给定两个长度分别为𝑛, 𝑚的序列𝐴, 𝐵,定义序列二元组(𝑢, 𝑣)是好的,当且 仅当满足 1. 𝑢为𝐴的子序列且𝑣为𝐵的子序列 2. 𝑢, 𝑣长度相同 3. 对于所有的1 ≤ 𝑖 ≤ 𝑢的长度,满足𝐴[𝑢[𝑖]] = 𝐵[𝑣[𝑖]] 两个二元组(𝑢, 𝑣), (𝑥, 𝑦)是不同的,当且仅当𝑢 ≠ 𝑥或𝑣 ≠ 𝑦。 现在希望你求出最大的𝐿,满足存在二元组(𝑢, 𝑣)使得𝑢的长度为𝐿且(𝑢, 𝑣)是 好的。并求出满足长度为𝐿的好的二元组的对数。答案对109 + 7取模。
Input Format
输入文件名为lcs.in。 输入文件包含两行字符串,分别表示序列𝐴, 𝐵。
Output Format
输出文件名为lcs.out。 输出文件包含两行。 第一行为𝐿。 第二个行为合法的二元组的对数对109 + 7取模的结果
Sample Output
【输入输出样例1】
lcs.in lcs.out abbcc bc 2 4
【输入输出样例2】
lcs.in lcs.out cbbdbb ccaaddacabdbdce 4 19
Hint
样例1 𝐿 = 2 用[𝑝1, 𝑝2, … , 𝑝𝑚]代表序列p,合法的二元组分别为: 1. ([2,4],[1,2]) 2. ([2,5],[1,2]) 3. ([3,4],[1,2]) 4. ([3,5],[1,2])
【数据规模与约定】
对于20%的数据,𝑛, 𝑚 ≤ 10 对于40%的数据,𝑛, 𝑚 ≤ 20 对于60%的数据,𝑛, 𝑚 ≤ 100 对于80%的数据,𝑛, 𝑚 ≤ 1000 对应100%的数据,𝑛, 𝑚 ≤ 5000,保证序列只包含小写字母。
Solution
首先是求LCS 非常经典的dp
i表示1~i的A序列,j表示1~j的B序列
l[i][j]=max(l[i-1][j],l[i][j-1]);
if (A[i]==B[i]) l[i][j]=l[i-1][j-1]+1;
然后要求最长公共子序列的对数
这里开一个数组 f[i][j]
表示1~i的A序列与1~j的B序列中最长的公共子序列对数
然后考虑转移
f的转移 是伴随的l的转移变化的
首先是(l[i][j-1],l[i-1][j])
这里要考虑的是当两个状态相等时,
我们需要把 f[i][j]加上f[i-1][j]以及f[i][j-1]
但是可能存在重复情况
我们需要去判断l[i-1][j]以及l[i][j-1]
是否都是由l[i-1][j-1]转移过来的
如果是,就减掉 f[i-1][j-1]也就是重复情况
然后就是A[i]==B[j]这一层的转移
如果 l[i][j]<l[i-1][j-1]+1 说明LCS长度增加
所以 f[i][j]直接从f[i-1][j-1] 转移
如果 l[i][j]==l[i-1][j-1]+1 说明增加了新的对数
所以 f[i][j]+=f[i-1][j-1];
(这个位置写的是+号,因为l[i][j]可能从l[i-1][j]和l[i][j-1]
等其他状态转移过来 所以应该把它们加在一起)
#include<cstdio> #include<cstring> const int yh=1e9+7; char a[5005],b[5005]; int f[5005][5005],l[5005][5005]; int lena,lenb; int main() { scanf("%s",a),lena=strlen(a); scanf("%s",b),lenb=strlen(b); for (int i=0;i<=lena;i++) f[i][0]=1; for (int j=0;j<=lenb;j++) f[0][j]=1; for (int i=1;i<=lena;i++) for (int j=1;j<=lenb;j++) { if (l[i-1][j]<l[i][j-1]) { l[i][j]=l[i][j-1]; f[i][j]=f[i][j-1]; } else if (l[i-1][j]>l[i][j-1]) { l[i][j]=l[i-1][j]; f[i][j]=f[i-1][j]; } else { l[i][j]=l[i][j-1]; f[i][j]=(f[i][j-1]+f[i-1][j])%yh; if (l[i-1][j-1]==l[i][j-1]) f[i][j]=(f[i][j]-f[i-1][j-1]+yh)%yh; } if (a[i-1]==b[j-1]) { if (l[i][j]<l[i-1][j-1]+1) { l[i][j]=l[i-1][j-1]+1; f[i][j]=f[i-1][j-1]; } else if (l[i][j]==l[i-1][j-1]+1) f[i][j]=(f[i][j]+f[i-1][j-1])%yh; } } printf("%d\n%d",l[lena][lenb],f[lena][lenb]); }