题意:给你n个禁止串,然后你只能用字符表的前m个字符去写一个无限长的串,要求是不能包含禁止串,而且串在后面不能出现循环
比赛的时候想的是先建一个自动机,然后将自动机确定化,不能到达的状态全部弄出来。但是对于剩下的状态就卡住了,我怎么才能知道这些状态会构成循环呢?后来看了别人的代码,看到了强连通分量,我就恍然大悟了。其实只需要对剩下的未确定的状态,根据转移边建图,然后跑一次强连通分量。
这么做的效果就是将原图剩下的状态缩成了一个DAG,我们每次只能由根结点往下走,我们必然需要停留在某个强连通分量里,不然的话我走到拓扑序最后的分量就不能再走了(或者说走到拓扑序最后的话,就只能在那个强连通分量沿着分量内的边走“自环”)。如果这个强连通分量是一个环的话,那么显然我们停留在这个强连通分量里的话就会有循环节。所以问题转化为是否存在一个强连通分量它不是一个环。判断方法就是维系一个大小为n的强连通分量至少需要n条边,只要强连通分量内的边大于n,它就不是一个自环。
#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; }