day16T2改错记

题目描述

给出\(n\)个串,你要从每个串中抽出一个子串(可以是空串),把他们拼接起来,问:

  1. 按字典序输出所有可能的结果(包括空串)
  2. 输出总共有多少种不同的结果(包括空串),对\(1e9+7\)取模

最后输入一个\(k\),为\(0\)时只回答第二问,为\(1\)时回答两个问

保证输入不超过\(1MB\),输出不超过\(200MB\)(就是说第二问真实值很大时不会要求回答第一问)

解析

要取出子串,容易想到后缀自动机

于是对每个串建出后缀自动机

然后考虑怎么表示“拼接”

不难发现,如果后面某个串中存在的字符可以在当前这个自动机内转移到,那么取后者不会漏掉任何结果,所以我们只用管每个节点在自己的自动机内转移不到的字符

即只有当不能在自己的串中转移时才考虑拼接后面的串

假设某个节点\(u\)没有转移到\(c\)的边,那么我们找到它后面的自动机中第一个能转移到\(c\)的根节点\(rt\),从\(u\)\(rt->trans[c]\)拉一条边,这样就表示了“拼接”的过程

第一问就从第一个串的根节点开始贪心地\(dfs\),过程中输出即可,此处可以信仰一波不会\(TLE\),不然光输出就\(GG\)了,想来不会有这种数据……

第二问就是问路径数了,拓扑排序\(dp\)即可

代码

为什么贴的是\(70pts\)代码呢?因为有\(3\)组数据输出实在太长,\(OJ\)判了所有人\(OLE\)……

1275607-20190319184054331-1880346480.png

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
#define MAXN 1000005

typedef long long LL;
const int mod = (int)1e9 + 7;
const char set[] = {'A', 'C', 'G', 'T'};
struct SAM {
    int idx, cnt, last, root[MAXN], next[MAXN << 1][4], link[MAXN << 1], maxlen[MAXN << 1], deg[MAXN << 1], top, dp[MAXN << 1];
    bool vis[MAXN << 1]; char cur[MAXN << 1];
    int newnode(int = 0);
    int add(int);
    void insert(char *);
    void prework();
    void prework(int, int);
    void dfs(int);
    void bfs();
};

int N, K;
char str[MAXN];
SAM sam;
int ans;

inline void inc(int &x, int y) { x += y; if (x >= mod) x -= mod; }
inline void dec(int &x, int y) { x -= y; if (x < 0) x += mod; }
inline int add(int x, int y) { x += y; return x >= mod ? x - mod : x; }
inline int sub(int x, int y) { x -= y; return x < 0 ? x + mod : x; }

int main() {
    freopen("copy.in", "r", stdin);
    freopen("copy.out", "w", stdout);

    scanf("%d", &N);
    for (int i = 1; i <= N; ++i) {
        scanf("%s", str);
        sam.insert(str);
    }
    scanf("%d", &K);
    sam.prework();
    if (K) sam.dfs(sam.root[1]);
    sam.bfs();
    printf("%d\n", ans);

    return 0;
}
int SAM::newnode(int x) {
    ++idx;
    if (x) {
        link[idx] = link[x], maxlen[idx] = maxlen[x];
        for (int i = 0; i < 4; ++i) next[idx][i] = next[x][i];
    }
    return idx;
}
int SAM::add(int c) {
    int np = newnode(), p = last;
    maxlen[np] = maxlen[last] + 1;
    while (p && !next[p][c]) next[p][c] = np, p = link[p];
    if (!p) link[np] = root[cnt];
    else {
        int q = next[p][c];
        if (maxlen[q] == maxlen[p] + 1) link[np] = q;
        else {
            int nq = newnode(q);
            maxlen[nq] = maxlen[p] + 1;
            link[q] = link[np] = nq;
            while (p && next[p][c] == q) next[p][c] = nq, p = link[p];
        }
    }
    return np;
}
void SAM::insert(char *s) {
    int n = strlen(s); last = root[++cnt] = newnode();
    for (int i = 0; i < n; ++i) last = add(std::lower_bound(set, set + 4, s[i]) - set);
}
void SAM::prework() {
    for (int i = cnt; i; --i) prework(root[i], root[i + 1]);
    for (int i = 1; i <= idx; ++i) for (int j = 0; j < 4; ++j) ++deg[next[i][j]];
    for (int i = 2; i <= cnt; ++i) for (int j = 0; j < 4; ++j) --deg[next[root[i]][j]];
}
void SAM::prework(int x, int y) {
    if (vis[x]) return;
    vis[x] = 1;
    for (int i = 0; i < 4; ++i)
        if (!next[x][i]) next[x][i] = next[y][i];
        else prework(next[x][i], y);
}
void SAM::dfs(int x) {
    cur[top] = '\0';
    printf("%s\n", cur);
    for (int i = 0; i < 4; ++i)
        if (next[x][i]) {
            cur[top++] = set[i];
            dfs(next[x][i]);
            --top;
        }
}
void SAM::bfs() {
    static int q[MAXN << 1], hd, tl;
    q[tl++] = root[1], dp[root[1]] = 1;
    while (hd < tl) {
        int p = q[hd++];
        if (!p) continue;
        inc(ans, dp[p]);
        for (int i = 0; i < 4; ++i) {
            inc(dp[next[p][i]], dp[p]);
            if (!(--deg[next[p][i]])) q[tl++] = next[p][i];
        }
    }
}
//Rhein_E 70pts

转载于:https://www.cnblogs.com/Rhein-E/p/10560540.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值