博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

【动态规划】【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]);
} 

 

posted @ 2017-10-21 23:44  JadeDark  阅读(356)  评论(0编辑  收藏  举报