1. 程式人生 > >「HAOI2010」最長公共子序列-DP

「HAOI2010」最長公共子序列-DP

Decription

給定兩個字串,求他們最長的公共子序列長度,以及最長公共子序列個數。

n 5000

Solution

最長公共序列直接 O ( n

2 ) DP即可。重點是如何求出最長公共子序列個數。

f i , j 表示第一個串匹配到

i ,第二個串匹配到 j 的最長公共子序列長度。 g i , j
則表示最長公共子序列個數。

考慮 f i , j 的轉移 f i , j = m a x ( f i 1 , j , f i , j 1 , ( f i 1 , j 1 + 1 ) × [ s i = t j ] )

那麼 g i , j 則加上 f i , j 轉移的來源的 g 即可。

但是有一個特殊情況:

f i 1 , j 1 = f i , j 時, g i 1 , j g i , j 1 重複轉移了 g i , j 因此要減去。

推薦FlashHu的題解,還有圖示。

// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std;

const int maxn = 5005, mod = 100000000;

int n, m, f[2][maxn], g[2][maxn];
char s[maxn], t[maxn];

void inc(int &a, int b)
{
    a += b;
    if (a >= mod) a -= mod;
}

void dec(int &a, int b)
{
    a -= b;
    if (a < 0) a += mod;
}

int main()
{
    scanf("%s", s + 1); n = strlen(s + 1) - 1;
    scanf("%s", t + 1); m = strlen(t + 1) - 1;

    fill(g[0], g[0] + m + 1, 1);
    for (int k = 1; k <= n; ++k) {
        int i = k & 1;
        g[i ^ 1][0] = g[i][0] = 1;
        for (int j = 1; j <= m; ++j) {
            f[i][j] = max(f[i ^ 1][j], f[i][j - 1]);
            if (s[k] == t[j]) f[i][j] = max(f[i][j], f[i ^ 1][j - 1] + 1);
        }
        for (int j = 1; j <= m; ++j) {
            g[i][j] = 0;
            if (f[i][j] == f[i ^ 1][j]) inc(g[i][j], g[i ^ 1][j]);
            if (f[i][j] == f[i][j - 1]) inc(g[i][j], g[i][j - 1]);
            if (f[i][j] == f[i ^ 1][j - 1])
                dec(g[i][j], g[i ^ 1][j - 1]);
            if (f[i][j] == f[i ^ 1][j - 1] + 1 && s[k] == t[j])
                inc(g[i][j], g[i ^ 1][j - 1]);
        }
    }

    printf("%d\n%d\n", f[n & 1][m], g[n & 1][m]);

    return 0;
}