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

题意:给你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;

}

 

 

你可能感兴趣的:(String)