AC自动机+数据结构 阿狸的打字机

题目链接

题意:中文题我就不多说什么了

以前也做过几个AC自动机+数据结构优化的题目,都是建好fail树然后就跟ac自动机无关了,所以这种题目都必须深刻理解AC自动机的原理以及fail树是怎么回事。

第一步:先读入字符串,建好AC自动机,我在这里被坑了,,,根据题目的特殊性,可以O(n)插入到字典树里面


第二步:建成fail树,预处理dfs序。


第三步: 离线回答询问。



联想暴力的算法,遍历每一个单词,每到一个节点,就一直fail fail,如果出现x的尾节点,就+1,。但这样子太暴力了,我们可以想一下,一直fail fail的过程,不就像一直往父亲节点的方向走么,如果将fail指针指向的点当成父亲几点建出一棵树,判断x在y中出现了几次,不就是判断字典树中root->y的路径中有几个点存在于x所在尾节点的子树中么,动态统计一棵子树中有几个点,就需要用数据结构来维护了。具体做的时候可以碰到一个Y的尾节点就处理所有的 对应的x的询问。


虽然调了好久,但其实是水题啊!!

#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
const int M = 100010;
const int CD = 26;
struct BIT{
	int c[M];
	void init() {
		memset(c,0,sizeof(c));
	}
	void update(int x,int d){
		for(;x<M;x+=x&-x) c[x] += d;
	}
	int sum(int x) {
		int ans  = 0;
		for(;x>0;x-=x&-x) ans += c[x];
		return ans;
	}
}bit;
struct Fail_tree {
	vector<int> Eg[M];
	int L[M] , R[M] ;
	int tfn ;
	void add(int a,int b)
	{
		Eg[a].push_back(b);
	}
	void dfs(int a)
	{
		L[a] = ++tfn;
		for(vector<int>::iterator it = Eg[a].begin();it!=Eg[a].end();it++) 
			dfs(*it);
		R[a] = tfn;
	}
}FT;
bool vis[M];
struct ACM {
	void init()
	{
		words = 0;
		fail[0] = 0; val[0] = 0;
		memset(ch[0],0,sizeof(ch[0]));
		sz = 1;
		for(int i = 0; i < 26; i++) ID[i+'a'] = i;
	}
	void Insert(char *s)
	{
		int p=0;
		for(;*s;s++){
			if(*s == 'P') {
				++words;
				hash[words] = p;
				name[p].push_back(words);
			} else if(*s == 'B') {
				p = fa[p];
			} else {
				int c = ID[*s];
				if(!ch[p][c]){
					memset(ch[sz],0,sizeof(ch[sz]));
					val[sz]=0;
					fa[sz] = p;
					ch[p][c]=sz++;
				}
				p=ch[p][c];
			}
		}
	}
	void Construct()
	{
		int  *s=Q,*e=Q;
		for(int i=0;i<CD;i++){
			if(ch[0][i]){
				fail[ch[0][i]] = 0;
				*e++ = ch[0][i];
			}
		}
		while(s!=e){
			int u = *s++;
			for(int i=0;i<CD;i++){
				int &v = ch[u][i];
				if(v){
					*e++ = v;
					fail[v]=ch[fail[u]][i];
				} else  {
					v=ch[fail[u]][i];
				}
			}
		}
	}
	void build()
	{
		init();
		Insert(str);
		Construct();
		FT.tfn = 0;
		for(int i = 1; i < sz; i++) 	FT.add(fail[i],i) ;
		FT.dfs(0);
	}
	void solve()
	{
		build();
		bit.init();
		int p = 0;
		for(int i = 0; str[i]; i++){
			if(str[i] == 'P') {
				if(vis[p]) continue;
				vis[p] = true;
				int tsz = name[p].size();
				for(int j = 0; j < tsz ; j++) {
					int y = name[p][j];
					int ysz = edge[y].size();
					for(int k = 0; k < ysz; k ++) {
						int word = edge[y][k].first ; 
						int id = edge[y][k].second;
						ans[id] = bit.sum(FT.R[hash[word]]) - bit.sum(FT.L[hash[word]] - 1) ;
					}
				}
			} else if(str[i] == 'B') {
				bit.update(FT.L[p],-1);
				p = fa[p];
			} else {
				p = ch[p][ID[str[i]]] ; 
				bit.update(FT.L[p],1);
			}
		}
		for(int i = 0; i < m; i++) printf("%d\n",ans[i]);
	}
	void input()
	{
		int  x , y;
		scanf("%s",str);
		scanf("%d",&m);
		for(int i = 0; i < m; i++) {
			scanf("%d%d",&x,&y);
			edge[y].push_back(make_pair(x,i));
		}
	}
	vector<int> name[M];
	int hash[M];
	int ans[M];
	vector<pair<int,int> > edge[M];
	int words;
	int tot;
	char ss[M];
	int m;
	int ID[128];
	int fail[M];
	int sz;
	int ch[M][CD];
	int Q[M];
	int val[M];
	int fa[M];
	char str[M];
}AC;
int main()
{
	AC.input();
	AC.solve();
	return  0;
}



你可能感兴趣的:(AC自动机+数据结构 阿狸的打字机)