zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Linux下Socket编程之地址结构
    矫正Fedora 8中livna源Nvidia驱动托付关连
    Firefox3 RC1颁布各种新特征发扬阐发更平定
    Fedora 8中完全开启compizfusion特效
    编译安置gsopcast SVN版
    Sopcast for linux更新至3.01!
    措置惩罚Fedora 8中的装备权限标题成绩
    vsftp假造用户设置(Ubuntu8.04)
    QQ2008贺岁版 on FedoraBy wine 0.9.58
    vFU NET
  • 原文地址:https://www.cnblogs.com/chanme/p/3669777.html
Copyright © 2011-2022 走看看