【CZY选讲·扩展LCS】

题目描述

给出两个仅有小写字母组成的字符串str1 和str2,试求出两个串的最长公共子序列。

数据范围

|str1| ⩽ 1000; |str2| ⩽ 10^6

题解:
    ①直接进行LCS(或者nlogn优化)爆炸了、

    ②尝试利用本体特点:|str1|很小。

    ③相当于答案不超过1000,那么就将数组下标和所存的东西调换,得到:

   f[i][j]表示匹配到s1[i],公共子序列长度为j时,s2匹配到的最小的位置。

   再记录next[i][j]表示当前在s2的i位置,下一个j字母出现的位置。

   转移方程:f[i+1][j]=min(f[i+1][j],f[i][j]) f[i+1][j+1]=min(f[i+1][j+1],next[f[i][j]+1][a[i+1]-'a'])

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <cmath>
#include <string>
#include <cstring>

#define st first
#define nd second
using namespace std;

typedef long long LL;

const int N = 1010;
const int M = 1E6 + 10;
const int INF = 1E9;
char a[N];
char b[M];
int f[N][N];
int nxt[M][26];
int n, m;

void solve() {
    f[0][1] = nxt[0][a[0] - 'a'];
    for (int i = 0; i <= n; ++i) f[i][0] = -1;
    for (int i = 0; i < n - 1; ++i)
        for (int j = 0; j <= n && f[i][j] < INF; ++j) {
            f[i + 1][j] = min(f[i + 1][j], f[i][j]);
            if (j < n)
                f[i + 1][j + 1] = min(f[i + 1][j + 1], nxt[f[i][j] + 1][a[i + 1] - 'a']);
        }
}

int main() {
    freopen("lcs.in", "r", stdin);
    freopen("lcs.out", "w", stdout);
    scanf("%s%s", a, b);
    n = strlen(a);
    m = strlen(b);
    for (int i = 0; i <= n; ++i)
        for (int j = 0; j <= n; ++j)
            f[i][j] = INF;
    for (int i = 0; i < 26; ++i)
        nxt[m][i] = INF;
    for (int i = m - 1; i >= 0; --i) {
        memcpy(nxt[i], nxt[i + 1], sizeof(nxt[i]));
        nxt[i][b[i] - 'a'] = i;
    }
    solve();
    int ans = 0;
    for (int i = n; i; --i)
        if (f[n - 1][i] < INF) {
            ans = i;
            break;
        }
    printf("%d
", ans);
}//czy020202

在撒满鲜血的天空迎着风飞舞,凭着一颗永不哭泣勇敢的心。————汪峰《勇敢的心》

原文地址:https://www.cnblogs.com/Damitu/p/7654403.html