首页 > 其他 > 详细

ZOJ3784 String of Infinity(AC自动机&&强连通分量)

时间:2014-04-17 12:18:35      阅读:752      评论:0      收藏:0      [点我收藏+]

题意:给你n个禁止串,然后你只能用字符表的前m个字符去写一个无限长的串,要求是不能包含禁止串,而且串在后面不能出现循环

比赛的时候想的是先建一个自动机,然后将自动机确定化,不能到达的状态全部弄出来。但是对于剩下的状态就卡住了,我怎么才能知道这些状态会构成循环呢?后来看了别人的代码,看到了强连通分量,我就恍然大悟了。其实只需要对剩下的未确定的状态,根据转移边建图,然后跑一次强连通分量。

这么做的效果就是将原图剩下的状态缩成了一个DAG,我们每次只能由根结点往下走,我们必然需要停留在某个强连通分量里,不然的话我走到拓扑序最后的分量就不能再走了(或者说走到拓扑序最后的话,就只能在那个强连通分量沿着分量内的边走“自环”)。如果这个强连通分量是一个环的话,那么显然我们停留在这个强连通分量里的话就会有循环节。所以问题转化为是否存在一个强连通分量它不是一个环。判断方法就是维系一个大小为n的强连通分量至少需要n条边,只要强连通分量内的边大于n,它就不是一个自环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#pragma warning(disable:4996)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include<queue>
#include<stack>
#include <cmath>
using namespace std;
 
#define maxn 210000
#define ll long long
 
int n, m;
 
struct Trie{
    Trie *fail, *go[26];
    bool ter; bool flag;
    void init(){
        memset(go, 0, sizeof(go)); fail = NULL; ter = false; flag = false;
    }
}pool[maxn], *root;
int tot;
 
void insert(char *c){
    int len = strlen(c); Trie *p = root;
    for (int i = 0; i < len; i++){
        if (p->go[c[i] - ‘a‘] != 0) p = p->go[c[i] - ‘a‘];
        else{
            pool[tot].init();
            p->go[c[i] - ‘a‘] = &pool[tot++];
            p = p->go[c[i] - ‘a‘];
        }
    }
    p->ter = true;
}
 
void getFail()
{
    queue<Trie*> que;
    que.push(root);
    root->fail = NULL;
    while (!que.empty()){
        Trie *temp = que.front(); que.pop();
        Trie *p = NULL;
        for (int i = 0; i < m; i++){
            if (temp->go[i] != NULL){
                if (temp == root) temp->go[i]->fail = root;
                else{
                    p = temp->fail;
                    while (p != NULL){
                        if (p->go[i] != NULL){
                            temp->go[i]->fail = p->go[i]; break;
                        }
                        p = p->fail;
                    }
                    if (p == NULL) temp->go[i]->fail = root;
                }
                que.push(temp->go[i]);
            }
        }
    }
}
 
bool ddfs(Trie *p){
    if (p == root||p==NULL) return false;
    if (p->flag == true) return p->ter;
    p->ter |= ddfs(p->fail); p->flag = true;
    return p->ter;
}
 
int pre[maxn], low[maxn], sccno[maxn],siz[maxn];
int sta[maxn],st;
int dfs_clock;
int scc_cnt;
 
int siz2[maxn];
 
void dfs(int u){
    low[u] = pre[u] = ++dfs_clock;
    sta[++st] = u;
    for (int i = 0; i < m; i++){
        Trie *p = pool[u].go[i];
        if (p->ter) continue;
        int v = p - pool;
        if (!pre[v]){
            dfs(v); low[u] = min(low[u], low[v]);
        }
        else if (!sccno[v]){
            low[u] = min(low[u], pre[v]);
        }
    }
    if (low[u] == pre[u]){
        ++scc_cnt;
        while (1){
            int x = sta[st--]; sccno[x] = scc_cnt;
            siz[scc_cnt]++;
            if (x == u) break;
        }
    }
}
void find_scc()
{
    memset(siz, 0, sizeof(siz));
    memset(sccno, 0, sizeof(sccno));
    memset(low, 0, sizeof(low));
    memset(pre, 0, sizeof(pre));
    st = dfs_clock = 0; scc_cnt = 0;
    for (int i = 0; i < tot; i++){
        if (pool[i].ter) continue;
        if (!pre[i]) dfs(i);
    }
}
 
char str[1500];
 
int main()
{
    int T; cin >> T;
    while (T--)
    {
        cin >> n >> m;
        tot = 0; root = &pool[tot++]; root->init();
        for (int i = 0; i < n; i++){
            scanf("%s", str);
            insert(str);
        }
        getFail();
        for (int i = 0; i < tot; i++) ddfs(&pool[i]);
        for (int i = 0; i < tot; i++){
            Trie *p = &pool[i];
            for (int k = 0; k < m; k++){
                if (p->go[k] == NULL){
                    Trie *temp = p; temp = temp->fail;
                    while (temp != NULL){
                        if (temp->go[k] != NULL) {
                            p->go[k] = temp->go[k]; break;
                        }
                        temp = temp->fail;
                    }
                    if (temp == NULL) p->go[k] = root;
                }
            }
        }
        find_scc();
        memset(siz2, 0, sizeof(siz2));
        for (int i = 0; i < tot; i++){
            if (pool[i].ter) continue;
            for (int j = 0; j < m; j++){
                int k = pool[i].go[j] - pool;
                if (sccno[k] == sccno[i]){
                    siz2[sccno[k]]++;
                }
            }
        }
        bool flag = false;
        for (int i = 1; i <= scc_cnt; i++){
            if (siz2[i]>siz[i]){
                flag = true; break;
            }
        }
        if (flag) puts("Yes");
        else puts("No");
    }
    return 0;
}

 

 

ZOJ3784 String of Infinity(AC自动机&&强连通分量),布布扣,bubuko.com

ZOJ3784 String of Infinity(AC自动机&&强连通分量)

原文:http://www.cnblogs.com/chanme/p/3669777.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!