• CF547E Mike and Friends(AC自动机的fail树dfs序上建立可持久化线段树)


    给定n个字符串s1,s2,...sn

    q次询问,第k个字符串在s[l,r]中出现了多少次。

    首先肯定是对所有的字符串建立AC自动机。

    然后,考虑弱化版本,第k个字符串在所有字符串中出现了多少次。

    先找到第k个字符串在字典树内对应的节点。

    然后,k的整个子树所表示的字符串,k都是它们的后缀。

    这样就可以先遍历每个字符串,它每个前缀对应的字典图节点点权+1。

    然后对k的整个子树,子树的点权和就是k在所有字符串中的出现次数。

    然后,加上询问条件[l,r]。

    即询问一个子树区间内编号在[l,r]之间的点权之和。

    可持久化线段树维护fail树的DFS序,然后处理每个询问即可。

    然后考虑每个模式串不相同的情况,一个子树节点可能对应多个编号,同时在可持久化线段树里更新即可。

    之前学可持久化的例题,现在终于悟了...

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+100;
    string s[maxn];
    vector<int> g[maxn];
    vector<int> gg[maxn];
    int tr[maxn][26],sz[maxn],fail[maxn],tot;
    int n,q;
    int ed[maxn];
    void insert (string s,int x) {
    	int u=0;
    	for (char i:s) {
    		if (!tr[u][i-'a']) tr[u][i-'a']=++tot;
    		u=tr[u][i-'a'];
    		gg[u].push_back(x);
    	}
    	ed[x]=u;
    } 
    void build () {
    	queue<int> q;
    	for (int i=0;i<26;i++) {
    		if (tr[0][i]) {
    			q.push(tr[0][i]);
    		}
    	}
    	while (q.size()) {
    		int u=q.front();
    		q.pop();
    		for (int i=0;i<26;i++) {
    			if (tr[u][i]) {
    				fail[tr[u][i]]=tr[fail[u]][i];
    				q.push(tr[u][i]);
    			}
    			else {
    				tr[u][i]=tr[fail[u]][i];
    			}
    		}
    	}
    }
    int dfn[maxn],id[maxn],cnt;
    void dfs (int u) {
    	sz[u]=1;
    	dfn[u]=++cnt;
    	id[cnt]=u;
    	for (int v:g[u]) {
    		dfs(v);
    		sz[u]+=sz[v];
    	}
    }
    
    const int M=maxn*32;
    int c[M],tol,lson[M],rson[M],T[maxn];
    int build (int l,int r) {
    	int rt=++tol;
    	c[rt]=0;
    	if (l==r) return rt;
    	int mid=(l+r)>>1;
    	lson[rt]=build(l,mid);
    	rson[rt]=build(mid+1,r);
    	return rt;
    }
    int up (int root,int l,int r,int p,int v) {
    	int newRoot=++tol;
    	if (l==r) {
    		c[newRoot]=c[root]+v;
    		return newRoot;
    	}
    	int mid=(l+r)>>1;
    	if (p<=mid) {
    		lson[newRoot]=up(lson[root],l,mid,p,v);
    		rson[newRoot]=rson[root];
    	}
    	else {
    		rson[newRoot]=up(rson[root],mid+1,r,p,v);
    		lson[newRoot]=lson[root];
    	}
    	c[newRoot]=c[lson[newRoot]]+c[rson[newRoot]];
    	return newRoot;
    }
    int query (int lr,int rr,int l,int r,int L,int R) {
    	if (l>=L&&r<=R) return c[rr]-c[lr];
    	int mid=(l+r)>>1;
    	int ans=0;
    	if (L<=mid) ans+=query(lson[lr],lson[rr],l,mid,L,R);
    	if (R>mid) ans+=query(rson[lr],rson[rr],mid+1,r,L,R);
    	return ans;
    }
    
    int main () {
    	ios::sync_with_stdio(false);
    	cin>>n>>q;
    	for (int i=1;i<=n;i++) {
    		cin>>s[i];
    		insert(s[i],i);
    	}
    	build();
    	for (int i=1;i<=tot;i++) g[fail[i]].push_back(i);
    	dfs(0);
    	T[0]=build(1,n);
    	for (int i=1;i<=cnt;i++) {
    		int x=id[i];
    		int tt=T[i-1];
    		for (int j:gg[x]) {
    			tt=up(tt,1,n,j,1);
    		}
    		T[i]=tt;
    	}
    	//printf("%d
    ",query(T[0],T[cnt],1,n,1,n));
    	while (q--) {
    		int k,l,r;
    		cin>>l>>r>>k; 
    		int ans=query(T[dfn[ed[k]]-1],T[dfn[ed[k]]+sz[ed[k]]-1],1,n,l,r);
    		//int ans=0;
    		//for (int i=dfn[ed[k]];i<=dfn[ed[k]]+sz[ed[k]]-1;i++) for (int j:gg[id[i]]) if (j>=l&&j<=r) ans++;
    		printf("%d
    ",ans);
     	}
    }
    
  • 相关阅读:
    Scala学习笔记(七):Rational、隐式转换、偏函数、闭包、重复参数及柯里化
    Java IO编程全解(三)——伪异步IO编程
    Java IO编程全解(二)——传统的BIO编程
    Java IO编程全解(一)——Java的I/O演进之路
    MyBatis+PageHelper实现分页
    Spring+SpringMVC+MyBatis深入学习及搭建(十七)——SpringMVC拦截器
    Spring+SpringMVC+MyBatis深入学习及搭建(十六)——SpringMVC注解开发(高级篇)
    Spring+SpringMVC+MyBatis深入学习及搭建(十五)——SpringMVC注解开发(基础篇)
    Spring+SpringMVC+MyBatis深入学习及搭建(十四)——SpringMVC和MyBatis整合
    Spring+SpringMVC+MyBatis深入学习及搭建(十三)——SpringMVC入门程序(二)
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15008597.html
Copyright © 2020-2023  润新知