[HNOI2006]最短母串

[HNOI2006]最短母串

https://ac.nowcoder.com/acm/problem/20049

分析

发现这个是与字串有关系的,就考虑后缀自动机和 自动机。我们发现对于这个问题后缀自动机使用会非常的不自然,因为我们并没有一个原串。我们就考虑如何用 自动机解决。我们可以把所有的目标串插入自动机。因为 所以结尾并没有太多状态,可以考虑状压 。令 为考虑到节点 ,现在状态为 的最小串。那么我们就只需要在 自动机上做转移就可以了,在构建 指针时,因为 指向的是当前串的最大后缀。所以我们有了第一个转移 。那么我们做一次 就可以满足长度最短,从小到大枚举字母就可以满足字典序最小这个条件。那么当我们找到第一个 其中 。那么证明它满足了所以串,这个时候递归输出就好了。每个节点的输出可以考虑数组模拟链表处理,否则空间复杂度可能过不去。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 2e3 + 100,M = 1e7 + 10;
int fail[N],ch[N][26],fa[M],A[M],f[N];
int size,n;
char S[N];
bool vis[N][20005];
#define P pair<int,int>
void insert(char *a,int id) {
    int len = strlen(a);int u = 0;
    for(int i = 0;i < len;i++) {
        int c = a[i] - 'A';
        if(!ch[u][c]) ch[u][c] = ++size;
        // cout <<a[i]<<" now: " << ch[u][c] << endl;
        u = ch[u][c];
    }

    f[u] |= (1<<id-1);
}
void build() {
    queue<int> q;
    for(int i = 0;i < 26;i++) if(ch[0][i]) q.push(ch[0][i]);
    while(!q.empty()) {
        int u = q.front();q.pop();
        for(int i = 0;i < 26;i++) {
            int v = ch[u][i];
            if(v) {
                fail[v] = ch[fail[u]][i];
                f[v] |= f[ch[fail[u]][i]];q.push(v);
            }
            else ch[u][i] = ch[fail[u]][i];
        }
    }
    // for(int i = 1;i <= size;i++) cout << fail[i] << " " << f[i] << endl;
}
void print(int x) {
    if(fa[x]) print(fa[x]);
    printf("%c",A[x]+'A');
}
void solve() {
    queue<P> q;int xzc = 0,Id = 0;
    vis[0][0] = 1;q.push(P(0,0));
    while(!q.empty()) {
        P x = q.front();q.pop();
        if(x.second == (1<<n)-1) {
            print(Id);printf("\n");
            return;
        }
        for(int i = 0;i < 26;i++) {
            int v = ch[x.first][i];
            int state = x.second|f[v];
            if(vis[v][state]) continue;
            vis[v][state] = 1;
            fa[++xzc] = Id;A[xzc] = i;
            q.push(P(v,state));
        }
        Id++;
    }
}
int main() {
    scanf("%d",&n);
    for(int i = 1;i <= n;i++) {
        scanf("%s",S);insert(S,i);
    }
    build();
    solve();
    return 0;
}
全部评论

相关推荐

杨柳哥:这不是普通人,那这个钱的是天才
点赞 评论 收藏
分享
球球别再泡了:坏,我单9要了14
点赞 评论 收藏
分享
昨天 14:22
门头沟学院 Java
大厂 测开 24*16离家近的事业编(大概只有大厂的1/4) 硕士
点赞 评论 收藏
分享
4 收藏 评论
分享
牛客网
牛客企业服务