洛谷 P2516 [HAOI2010]最长公共子序列

字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xi = yj。例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列。对给定的两个字符序列,求出他们最长的公共子序列长度,以及最长公共子序列个数。

其实就是让你求一个最长公共子序列的长度和方案数

因为长度最大是5000,所以考虑dp

\(f_{i,j}\)表示第一个字符串a的前1...i的子串和第二个字符串b的前1...j的子串的最长公共子序列长度

那么有两种情况

\[f_{i,j}=\begin{cases}f_{i-1,j-1}&\; (a_i=b_j) \\ max(f_{i-1,j},f_{i,j-1})&\; (a_i\ne b_j)\end{cases} \]

然后就可以\(O(n^2)\)转移了

至于方案数也使用一样统计,开一个\(dp_{i,j}\)表示第一个字符串a的前1...i的子串和第二个字符串b的前1...j的子串的最长公共子序列的方案数

我们只需要统计\(f_{i-1,j}\)\(f_{i,j-1}\)中与\(f_{i,j}\)相等的把方案数相加,然后又有两种情况

\[dp_{i,j}+=\begin{cases}dp_{i-1,j-1}&\, (a_i=b_j\ \& \ f_{i,j}=f_{i-1,j-1}+1) \\ -dp_{i-1,j-1}&\, (a_i\ne b_j\ \& \ f_{i,j}=f_{i-1,j-1})\end{cases} \]

负数的情况就是减去算重的一次

初始化就是\(dp_{i,0}=dp_{0,j}=1\quad(0\le i\le len_a,0\le j\le len_b)\)

然后这题还卡空间(真是毒瘤

但是这个转移方程滚动还是很显然的,第一维只留两个状态就可以了,然后算答案和统计方案数也可以放在一起做

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 5000;
const int p = 1e8;
using namespace std;
int n,la,lb,dp[3][N + 5],f[3][N + 5];
char a[N + 5],b[N + 5];
int main()
{
    scanf("%s",a + 1);
    scanf("%s",b + 1);
    la = strlen(a + 1);
    lb = strlen(b + 1);
    la--;
    lb--;
    for (int i = 0;i <= lb;i++)
        dp[0][i] = 1;
    dp[1][0] = 1;    
    for (int i = 1;i <= la;i++)
        for (int j = 1;j <= lb;j++)
        {
            dp[i % 2][j] = 0;
            if (a[i] == b[j])
            {
                f[i % 2][j] = f[(i - 1) % 2][j - 1] + 1;
                dp[i % 2][j] += dp[(i - 1) % 2][j - 1]; 
            }
            else
                f[i % 2][j] = max(f[i % 2][j - 1],f[(i - 1) % 2][j]);
            if (f[(i - 1) % 2][j] == f[i % 2][j])
                dp[i % 2][j] += dp[(i - 1) % 2][j];
            if (f[i % 2][j - 1] == f[i % 2][j])
                dp[i % 2][j] += dp[i % 2][j - 1];
            if (f[i % 2][j] == f[(i - 1) % 2][j - 1] && a[i] != b[j])
                dp[i % 2][j] -= dp[(i - 1) % 2][j - 1];                
            dp[i % 2][j] = (dp[i % 2][j] + p) % p;            
        }
    printf("%d\n%d",f[la % 2][lb],dp[la % 2][lb]);
    return 0;
}
posted @ 2020-06-08 20:33  eee_hoho  阅读(233)  评论(0编辑  收藏  举报