P4608 [FJOI2016] 序列自动机

题意

传送门 P4608 [FJOI2016]所有公共子序列问题

题解

序列自动机,原理即记录序列各个位置对应的每一个字符集元素的下一个出现的位置,则从根节点 D F S DFS DFS 即可遍历所有子序列。对于公共子序列问题,将序列 X , Y X, Y X,Y 处理为序列自动机,从根节点沿相同路径 D F S DFS DFS,则遍历的所有路径对应了所有的公共子序列。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxc 58
#define maxn 3015
#define maxl 20
typedef long long ll;
const ll base = 1e18;
struct bigInt
{
    int len;
    ll *a;
    void init()
    {
        a = new ll[20], len = 0;
        memset(a, 0, sizeof(ll) * 20);
    }
    void operator++()
    {
        int i = 0;
        ++a[i];
        while (a[i] >= base)
            ++a[i + 1], a[i] -= base, ++i;
        while (a[len])
            ++len;
    }
    void operator+=(bigInt &b)
    {
        int len2 = max(len, b.len);
        for (int i = 0; i < len2; i++)
        {
            a[i] += b.a[i];
            if (a[i] >= base)
                ++a[i + 1], a[i] -= base;
        }
        len = len2 + (a[len2] ? 1 : 0);
    }
    void output()
    {
        for (int i = len - 1; i >= 0; --i)
            printf("%lld", a[i]);
    }
} dp[maxn][maxn];
int N, M, K, p, nxt_x[maxn][maxc], nxt_y[maxn][maxc];
char X[maxn], Y[maxn], tmp[maxn];

void init(char *s, int len, int nxt[maxn][maxc])
{
    for (int i = len; i; --i)
    {
        memcpy(nxt[i - 1], nxt[i], sizeof(nxt[i]));
        nxt[i - 1][s[i] - 'A'] = i;
    }
}

void pdfs(int x, int y)
{
    printf("%s\n", tmp);
    for (int i = 0; i < maxc; i++)
    {
        if (nxt_x[x][i] && nxt_y[y][i])
        {
            tmp[p++] = 'A' + i;
            pdfs(nxt_x[x][i], nxt_y[y][i]);
            tmp[--p] = '\0';
        }
    }
}

void cdfs(int x, int y)
{
    if (dp[x][y].len)
        return;
    dp[x][y].init();
    ++dp[x][y];
    for (int i = 0; i < maxc; i++)
    {
        if (nxt_x[x][i] && nxt_y[y][i])
        {
            cdfs(nxt_x[x][i], nxt_y[y][i]);
            dp[x][y] += dp[nxt_x[x][i]][nxt_y[y][i]];
        }
    }
}

int main()
{
    scanf("%d%d", &M, &N);
    scanf("%s%s", X + 1, Y + 1);
    scanf("%d", &K);
    init(X, M, nxt_x);
    init(Y, N, nxt_y);
    if (K)
    {
        pdfs(0, 0);
    }
    cdfs(0, 0);
    dp[0][0].output();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值