• [NOI2011]阿狸的打字机(AC自动机)


    题意

    给定n个字符串,m次询问((n,m leq 1e5))每次询问一个串在另一串的出现次数,可以离线处理

    思路

    首先肯定是对n个串建AC自动机

    如何求一个串的子串?假设i点对应这个字符串,那么从fail[ i ]就表示i串的最长后缀,一直向上跳fail,就可以得到i串的所有后缀。对于i的所有子串,我们可以理解为i的所有后缀的所有前缀,即每个后缀的前缀对应一个子串,他们一一对应
    由于i在AC自动机上的父亲就是它的前缀,而一个点跳fail又可以跳出所有后缀,所以求i的子串等价于求i点到根节点的路径上所有点分别跳fail得到的结果

    然而,一步步跳fail显然太暴力,对于aaaaaaa...这种数据很显然过不了,这个时候可以考虑fail树。由于j串对应的点只有一个,而如果i的一个前缀对应的节点p在j的子树中,那么p一定可以通过跳fail跳到j,此时ans应该加一

    子树求和?

    这时就可以想到离线做法了,dfs一遍原trie树,进入一个点将它在fail树上的权值加一,退出时减去,如果dfs到i点,那么就统计子树目标j在fail树上的子树和即为答案,子树求和怎么做?遍历fail树求一遍dfn序即可用树状数组维护

    Code:

    #include<bits/stdc++.h>
    #define N 100005
    #define lowbit(x) ((x)&(-x))
    using namespace std;
    int n,m,nxt[N][26],nnxt[N][26],fail[N],end[N],fa[N],hfu,ndsum;
    int dfn[N],size[N],col;
    int ans[N],c[N];
    char a[N],b[N];int top;
    struct Node {int son,i;};
    struct Edge
    {
    	int next,to;
    }edge[N<<1];int head[N],cnt;
    vector <Node> nd[N];
    
    void add_edge(int from,int to)
    {
    	edge[++cnt].next=head[from];
    	edge[cnt].to=to;
    	head[from]=cnt;
    }
    void read(int &x)
    {
    	char c;int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
    }
    void update(int x,int val) {for(;x<=col;x+=lowbit(x)) c[x]+=val;}
    int query(int x) {int ret=0;for(;x;x-=lowbit(x)) ret+=c[x];return ret;}
    int ask(int l,int r) {return query(r)-query(l-1);}
    void get_fail()
    {
    	queue<int> q;
    	for(int i=0;i<26;++i) if(nxt[0][i]) q.push(nxt[0][i]);
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		for(int i=0;i<26;++i)
    		{
    			if(nxt[u][i])
    			{
    				q.push(nxt[u][i]);
    				fail[nxt[u][i]]=nxt[fail[u]][i];
    			}
    			else nxt[u][i]=nxt[fail[u]][i];
    		}
    	}
    }
    void work()
    {
    	int len=strlen(a),now=0;
    	for(int i=0;i<len;++i)
    	{
    		if(a[i]!='B'&&a[i]!='P')
    		{
    			if(!nxt[now][a[i]-'a']) nxt[now][a[i]-'a']=++ndsum,fa[ndsum]=now;
    			now=nxt[now][a[i]-'a'];
    		}
    		if(a[i]=='B') now=fa[now];
    		if(a[i]=='P') end[++hfu]=now;
    	}
    }
    void dfs(int rt)//fail dfn
    {
    	size[rt]=1;
    	dfn[rt]=++col;
    	for(int i=head[rt];i;i=edge[i].next) {dfs(edge[i].to);size[rt]+=size[edge[i].to];}
    }
    void dfs1(int rt)//solve
    {
    	update(dfn[rt],1);
    	if(!nd[rt].empty())
    	{
    		for(int i=0;i<(int)nd[rt].size();++i)
    		{
    			int pt=dfn[nd[rt][i].son];
    			ans[nd[rt][i].i]=ask(pt,pt+size[nd[rt][i].son]-1);
    		}
    	}
    	for(int i=0;i<26;++i) if(nnxt[rt][i]) dfs1(nnxt[rt][i]);
    	update(dfn[rt],-1);
    }
    int main()
    {
    	scanf("%s",a);
    	work();
    	for(int i=0;i<=ndsum;++i)
    	for(int j=0;j<26;++j) nnxt[i][j]=nxt[i][j];
    	get_fail();
    	read(m);
    	for(int i=1;i<=m;++i)
    	{
    		int x,y;
    		read(x);read(y);
    		nd[end[y]].push_back((Node){end[x],i});
    	}
    	for(int i=1;i<=ndsum;++i) add_edge(fail[i],i);
    	dfs(0);
    	dfs1(0);
    	for(int i=1;i<=m;++i) printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    火币Huobi API Websocket
    火币Huobi API
    OKEX API(Websocket)
    OKEX API
    Linux下Miniconda量化环境安装
    Numba:高性能Python编译器
    十进制和十六进制互相转换
    JavaScript 原型和原型链
    Redux 进阶之 react-redux 和 redux-thunk 的应用
    Vue 中 $nextTick() 的应用
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11228653.html
Copyright © 2020-2023  润新知