Kattis taboo (AC自动机 拓扑排序 DP)

题目链接:点击这里

题意:给出n个01串,要构造一个最长的串使得这个串不包含所有出现过的串,无解输出-1.

首先把所有的点扔进自动机,因为出现过的串不能在我们构造的串中出现,所以事先把某些”坏点”标记,坏点指的就是每个串的结束节点以及沿着结束节点fail指针走下去的节点. 如果剩下的图中能够沿着根走出一个环那么必然就无解了, 判环可以用一遍拓扑确定. 排除无解情况剩下就是一个DAG了, 用 dp[i][j] 表示在自动机上i节点, 串长为j是否可行, 最后按照最长路打印方案即可.

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <cmath>
#include <algorithm>
#include <stack>
#define Clear(x,y) memset (x,y,sizeof(x))
#define fi first
#define se second
#define pii pair<int, int>
#define pli pair<long long, int>
#define pb push_back
#define mod 1000000007
template <class T>
inline bool scan (T &ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; //EOF
    while (c != '-' && (c < '0' || c > '9') ) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}
using namespace std;
#define maxn 500005

int G[maxn][2];
void add_edge (int u, int v, int w) {
    G[u][w] = v;
}

struct trie {
    int next[maxn][2], fail[maxn];
    long long end[maxn];
    int root, cnt;
    int new_node () {
        memset (next[cnt], -1, sizeof next[cnt]);
        end[cnt++] = 0;
        return cnt-1;
    }
    void init () {
        cnt = 0;
        root = new_node ();
    }
    void insert (char *buf) {//字典树插入一个单词
        int len = strlen (buf);
        int now = root;
        for (int i = 0; i < len; i++) {
            int id = buf[i]-'0';
            if (next[now][id] == -1) {
                next[now][id] = new_node ();
            }
            now = next[now][id];
            if (end[now]) break;
        }
        end[now]++;
    }
    void build () {//构建fail指针
        queue <int> q;
        fail[root] = root;
        for (int i = 0; i < 2; i++) {
            if (next[root][i] == -1) {
                next[root][i] = root;
            }
            else {
                fail[next[root][i]] = root;
                q.push (next[root][i]);
            }
        }
        while (!q.empty ()) {
            int now = q.front (); q.pop ();
            end[now] += end[fail[now]];
            for (int i = 0; i < 2; i++) {
                if (next[now][i] == -1) {
                    next[now][i] = next[fail[now]][i];
                }
                else {
                    fail[next[now][i]] = next[fail[now]][i];
                    q.push (next[now][i]);
                }
            }
        }
    }
    int in[maxn], in2[maxn];
    bool vis[maxn], inq[maxn];
    void dfs1 (int u) {
        for (int id = 0; id < 2; id++) {
            int j = next[u][id];
            if (end[j]) continue;
            in[j]++;
            in2[j]++;
            if (in[j] == 1) dfs1 (j);
        }
    }
    bool topo () {///判环
        Clear (inq, 0);
        Clear (vis, 0);
        Clear (in, 0);
        Clear (in2, 0);
        int tot = 0, num = 0;
        dfs1 (0);
        queue <int> q; while (!q.empty ()) q.pop ();
        if (in[0]) return 1;
        inq[0] = 1, vis[0] = 1;
        q.push (0); num++;
        while (!q.empty ()) {
            int u = q.front (); q.pop ();
            for (int id = 0; id < 2; id++) {
                int v = next[u][id]; if (end[v]) continue;
                in[v]--;
                vis[v] = 1;
                if (!in[v]) {
                    q.push (v);
                    inq[v] = 1;
                    num++;
                }
            }
        }
        for (int i = 0; i < cnt; i++) {
            if (inq[i] != vis[i]) {
                return 1;
            }
        }
        return 0;
    }
    int dp[maxn], to[maxn], Max;
    int pre[maxn];

    void dfs (int u) {
        for (int id = 0; id < 2; id++) {
            int v = next[u][id]; if (end[v]) continue;
            dfs (v);
            to[u] = max (to[u], to[v]+1);
            if (to[v]+dp[u]+1 == Max) {
                add_edge (u, v, id);
            }
        }
    }
    void query () {
        if (topo ()) {
            printf ("-1\n");
            return ;
        }
        Clear (dp, 0);
        queue <int> q; while (!q.empty ()) q.pop ();
        q.push (0);
        Max = 0;
        while (!q.empty ()) {
            int u = q.front (); q.pop ();
            for (int id = 0; id < 2; id++) {
                int j = next[u][id]; if (end[j]) continue;
                if (dp[j] < dp[u]+1) {
                    dp[j] = dp[u]+1;
                }
                in2[j]--;
                if (!in2[j]) {
                    q.push (j);
                    Max = max (Max, dp[j]);
                }
            }
        }
        Clear (G, -1);
        Clear (to, 0);
        dfs (0);
        int u = 0;
        while (dp[u] != Max) {
            if (G[u][0] != -1) {
                printf ("0");
                u = G[u][0];
            }
            else {
                printf ("1");
                u = G[u][1];
            }
        }
        printf ("\n");
    }
}ac;

int n;
char buf[maxn];

int main () {
    scanf ("%d", &n);
    ac.init ();
    for (int i = 0; i < n; i++) {
        scanf ("%s", buf);
        ac.insert (buf);
    }
    ac.build ();
    ac.query ();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值