NOIP模拟10-21最长公共子序列

Posted xCatRisk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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,保证序列只包含小写字母。

思路

首先是求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]);
} 

 

以上是关于NOIP模拟10-21最长公共子序列的主要内容,如果未能解决你的问题,请参考以下文章

Jzoj 4889NOIP2016提高A组集训第14场11.12暴力贪心最长公共回文子序列

NOIp模拟赛String Master

动规,模拟,递推,最长公共子序列

2021.8.9提高B组模拟1T1 最长公共回文子序列(dfs)

2021.8.9提高B组模拟1T1 最长公共回文子序列(dfs)

NOIP2017模拟赛 senior 6.29 T2 小T的钢琴(piano)