zoukankan      html  css  js  c++  java
  • bzoj4231回忆树——AC自动机

    bzoj4231 回忆树

    给定一颗Trie树,每次询问u到v的路径上构成的字符串包含了给定模式串T多少次。

    思路

    考虑离线之后对于所有的模式串建立AC自动机,考虑跨过lca的串范围有限,直接暴力kmp即可。

    然后我们就将一次询问拆成了两条链了,可以将每次询问挂在链上然后离线去dfs,每次dfs时将这个节点添加进AC自动机匹配,对于一段u到v的链,我们在链头的某一个位置减去之前不合法的匹配数量,再在链尾加上总的匹配数量就好了。

    如何用ac自动机来记录某个固定的串匹配了多少次的话,可以建立fail树之后用BIT来动态维护子树和。

    /*=======================================
     * Author : ylsoi
     * Time : 2019.7.2
     * Problem : bzoj4231
     * E-mail : ylsoi@foxmail.com
     * ====================================*/
    #include<bits/stdc++.h>
    
    #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
    #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
    #define debug(x) cout<<#x<<"="<<x<<" "
    #define DEBUG(x) cerr<<#x<<"="<<x<<" "
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    #define pii pair<int,int>
    typedef long long ll;
    
    using namespace std;
    
    void File(){
    	freopen("bzoj4231.in","r",stdin);
    	freopen("bzoj4231.out","w",stdout);
    }
    
    template<typename T>void read(T &_){
    	_=0; T f=1; char c=getchar();
    	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    	for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    	_*=f;
    }
    
    string proc(){
    	ifstream f("/proc/self/status");
    	return string(istreambuf_iterator<char>(f),istreambuf_iterator<char>());
    }
    
    const int maxn=3e5+10;
    int n,m,ans[maxn];
    int beg[maxn],to[maxn<<1],las[maxn<<1],cha[maxn<<1],cnte=1;
    
    void add(int u,int v,int c){
    	las[++cnte]=beg[u],beg[u]=cnte,to[cnte]=v,cha[cnte]=c;
    	las[++cnte]=beg[v],beg[v]=cnte,to[cnte]=u,cha[cnte]=c;
    }
    
    namespace aca{
    	int ch[maxn][26],fail[maxn],cnt;
    	int dfn[maxn],sz[maxn],cnt_dfn;
    	int sum[maxn];
    	int lowbit(int x){return x&(-x);}
    	vector<int>G[maxn];
    	int insert(int len,char *t){
    		int o=0,c;
    		REP(i,1,len){
    			c=t[i]-'a';
    			if(!ch[o][c])ch[o][c]=++cnt;
    			o=ch[o][c];
    		}
    		return o;
    	}
    	void get_fail(){
    		queue<int>qu;
    		REP(i,0,25)if(ch[0][i])qu.push(ch[0][i]);
    		while(!qu.empty()){
    			int u=qu.front(); qu.pop();
    			REP(i,0,25){
    				int v=ch[u][i];
    				if(v)fail[v]=ch[fail[u]][i],qu.push(v);
    				else ch[u][i]=ch[fail[u]][i];
    			}
    		}
    		REP(i,1,cnt)G[fail[i]].push_back(i);
    	}
    	void dfs(int u){
    		dfn[u]=++cnt_dfn,sz[u]=1;
    		REP(i,0,G[u].size()-1){
    			int v=G[u][i];
    			dfs(v),sz[u]+=sz[v];
    		}
    	}
    	void update(int u,int x){
    		u=dfn[u];
    		for(;u<=cnt_dfn;u+=lowbit(u))sum[u]+=x;
    	}
    	int query(int u){
    		int l=dfn[u]-1,r=dfn[u]+sz[u]-1,ret=0;
    		for(;l>=1;l-=lowbit(l))ret-=sum[l];
    		for(;r>=1;r-=lowbit(r))ret+=sum[r];
    		return ret;
    	}
    }
    
    int st[maxn][21],Log[maxn],dep[maxn],fa_ch[maxn];
    
    void dfs(int u,int fh){
    	st[u][0]=fh,dep[u]=dep[fh]+1;
    	for(int i=beg[u];i;i=las[i]){
    		int v=to[i];
    		if(v==fh)continue;
    		fa_ch[v]=cha[i];
    		dfs(v,u);
    	}
    }
    
    int lca(int x,int y){
    	if(dep[x]<dep[y])swap(x,y);
    	for(int d=Log[dep[x]-dep[y]];dep[x]!=dep[y];--d)
    		if(dep[st[x][d]]>=dep[y])x=st[x][d];
    	if(x==y)return x;
    	for(int d=Log[dep[x]];d>=0;--d)
    		if(st[x][d]!=st[y][d])
    			x=st[x][d],y=st[y][d];
    	return st[x][0];
    }
    
    int kth(int x,int k){
    	if(k<0)return x;
    	REP(i,0,Log[k])if(1<<i&k)x=st[x][i];
    	return x;
    }
    
    int kmp(char *s,char *t,int lens,int lent){
    	if(lens<lent)return 0;
    	reverse(s,s+lens);
    	static int fail[maxn];
    	fail[0]=fail[1]=0;
    	REP(i,1,lent-1){
    		int j=fail[i];
    		while(j && t[j]!=t[i])j=fail[j];
    		if(t[j]==t[i])fail[i+1]=j+1;
    		else fail[i+1]=0;
    	}
    	int j=0,ret=0;
    	REP(i,0,lens-1){
    		while(j && s[i]!=t[j])j=fail[j];
    		if(s[i]==t[j])++j;
    		if(j==lent)++ret;
    	}
    	return ret;
    }
    
    struct node{int id,o,ty;};
    vector<node>qu[maxn];
    
    void solve(int u,int o,int fh){
    	using namespace aca;
    	if(u!=1){
    		int c=fa_ch[u];
    		o=ch[o][c];
    		update(o,1);
    	}
    	REP(i,0,qu[u].size()-1){
    		ans[qu[u][i].id]+=qu[u][i].ty*query(qu[u][i].o);
    	}
    	for(int i=beg[u];i;i=las[i]){
    		int v=to[i];
    		if(v==fh)continue;
    		solve(v,o,u);
    	}
    	if(u!=1)update(o,-1);
    }
    
    int main(){
    	File();
    
    	read(n),read(m);
    
    	int u,v,anc;
    	static char s[maxn],t[maxn];
    	REP(i,2,n){
    		read(u),read(v),scanf("%s",t);
    		add(u,v,t[0]-'a');
    	}
    	dfs(1,0);
    	REP(i,2,n)Log[i]=Log[i>>1]+1;
    	REP(j,1,Log[n])REP(i,1,n)
    		if(dep[i]-(1<<j)>=1)
    			st[i][j]=st[st[i][j-1]][j-1];
    
    
    	REP(i,1,m){
    		read(u),read(v);
    		scanf("%s",t+1);
    		int len=strlen(t+1);
    		anc=lca(u,v);
    		int p1=aca::insert(len,t);
    		reverse(t+1,t+len+1);
    		int p2=aca::insert(len,t);
    		int cnt=0;
    		int u1=kth(u,dep[u]-dep[anc]-len+1),u2=u1;
    		REP(j,1,dep[u1]-dep[anc])s[++cnt]='a'+fa_ch[u2],u2=st[u2][0];
    		if(dep[u]-dep[anc]>=len){
    			qu[u1].push_back((node){i,p2,-1});
    			qu[u].push_back((node){i,p2,1});
    		}
    		int v1=kth(v,dep[v]-dep[anc]-len+1),v2=v1;
    		cnt+=dep[v1]-dep[anc];
    		REP(j,1,dep[v1]-dep[anc])s[cnt-j+1]='a'+fa_ch[v2],v2=st[v2][0];
    		if(dep[v]-dep[anc]>=len){
    			qu[v1].push_back((node){i,p1,-1});
    			qu[v].push_back((node){i,p1,1});
    		}
    		s[cnt+1]='';
    		ans[i]+=kmp(s+1,t+1,cnt,len);
    	}
    
    	aca::get_fail();
    	aca::dfs(0);
    
    	solve(1,0,0);
    
    	REP(i,1,m)printf("%d
    ",ans[i]);
    
    	return 0;
    }
    
    
  • 相关阅读:
    java servlet手机app访问接口(三)高德地图云存储及检索
    java servlet手机app访问接口(二)短信验证
    java servlet手机app访问接口(一)数据加密传输验证
    java Servlet+mysql 调用带有输入参数和返回值的存储过程(原创)
    java servlet Json.org.jar
    java Servlet(续)
    [HAOI2015]树上操作
    [USACO15DEC]最大流Max Flow
    [NOI2015]软件包管理器
    [洛谷P3384]【模板】树链剖分
  • 原文地址:https://www.cnblogs.com/ylsoi/p/11122931.html
Copyright © 2011-2022 走看看