[HAOI2010][DP]最长公共子序列

来源:互联网 发布:58网络经纪人 编辑:程序博客网 时间:2024/06/05 19:23
[Problem 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的一个子序列。
对给定的两个字符序列,求出他们最长的公共子序列长度,以及最长公共子序列个数。
[Algorithm]
DP 容斥
[Analysis]
最长公共子序列是一个经典的dp模型。但是加上统计个数就比较麻烦。我们可以很容易的想到这样的递推式
when s[i] == s[j]
f[i][j] = f[i - 1][j - 1] + 1
way[i][j] = way[i - 1][j - 1]
way[i][j] += way[i][j - 1] (f[i][j] == f[i][j - 1)
way[i][j] += way[i - 1][j] (f[i][j] == f[i - 1][j])
when s[i] != s[j]
f[i][j] = max(f[i - 1][j], f[i][j - 1])
way[i][j] = 0;
way[i][j] += way[i][j - 1](f[i][j] == f[i][j - 1])
way[i][j] += way[i - 1][j](f[i][j] == f[i - 1][j])
第一种情况是没有问题的,但第二种情况有问题。如果f[i][j - 1]和f[i - 1][j]都是由f[i - 1][j - 1]推出来的,那么way[i][j]里面相当于加了两遍的way[i - 1][j - 1],在这种情况下要加上way[i - 1][j - 1],这样就没有问题了。
[Pay Attention]
想了半天没有想到会有重复的情况,还是看了别人的题解才知道的……所以对于这种拿不准的转移要多想想,实在不行多出几组数据或者写个暴力对拍……还有一个蛋疼的地方,题目要求mod的数是1e8,我看成了1e9……要小心
[Code]
/**************************************************************    Problem: 2423    User: gaotianyu1350    Language: C++    Result: Accepted    Time:1668 ms    Memory:1352 kb****************************************************************/ #include <cstdio>#include <cmath>#include <cstring>#include <cstdlib>#include <iostream>using namespace std; const int MOD = (int)1e8;const int MAXN = 5010; int f[2][MAXN] = {{0}}, way[2][MAXN] = {{0}};string s1, s2; inline void update(int &maxLen, int &maxWay, int len, int way){    if (len > maxLen)    {        maxLen = len;        maxWay = way;    }    else if (len == maxLen)        maxWay = (maxWay + way) % MOD;} int main(){    //freopen("input.txt", "r", stdin);    int pre = 0, now = 1;    getline(cin, s1, '.');    getchar();    getline(cin, s2, '.');    int len1 = s1.length(), len2 = s2.length();    for (int i = 0; i <= len2; i++)        way[pre][i] = 1;    way[now][0] = 1;    for (int i = 1; i <= len1; i++)    {        for (int j = 1; j <= len2; j++)        {            f[now][j] = f[now][j - 1];            way[now][j] = way[now][j - 1];            update(f[now][j], way[now][j], f[pre][j], way[pre][j]);            if (s1[i - 1] == s2[j - 1])                update(f[now][j], way[now][j], f[pre][j - 1] + 1, way[pre][j - 1]);            if (s1[i - 1] != s2[j - 1] && f[now][j - 1] == f[pre][j] && f[pre][j - 1] == f[now][j])                way[now][j] = (way[now][j] + MOD - way[pre][j - 1]) % MOD;        }        pre ^= 1;        now ^= 1;    }    printf("%d\n%d\n", f[pre][len2], way[pre][len2]);}


0 0