zoukankan      html  css  js  c++  java
  • CF666E Forensic Examination(后缀自动机,可持久化线段树合并)

    给你一个串\(S\),以及一个字符串数组\(T_{1,2,...m}\)\(q\)次询问,每次问\(S\)的子串\(S[p_l,...p_r]\)\(T_{l...r}\)中的哪个串的出现次数最多,并输出出现次数。

    做法:

    对串\(S\)和数组\(T\)建立后缀自动机。

    在后缀自动机上找到\(S[l,r]\)这个子串对应的节点u,这是一个经典操作。

    数组\(T\)内每个字符串都可以视作一种颜色。

    现在问题转变为,每次询问一个节点,节点的link树子树内部哪种颜色出现次数最多,这个次数是多少。

    这里可以线段树合并,或者树上启发式合并来做。

    这里选择用线段树合并的做法。

    时间复杂度\(O(nlogn)\)

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=6e5+10;
    int m,q;
    int n,len[maxn<<1],link[maxn<<1],nxt[maxn<<1][26],tot=1,lst=1;
    string s;
    string t[maxn];
    void sam_extend (char c) {
    	int cur=++tot,p=lst;
    	len[cur]=len[lst]+1;
    	while (p&&!nxt[p][c-'a']) {
    		nxt[p][c-'a']=cur;
    		p=link[p];
    	}
    	if (!p) link[cur]=1;
    	else {
    		int q=nxt[p][c-'a'];
    		if (len[p]+1==len[q]) {
    			link[cur]=q;
    		}
    		else {
    			int clone=++tot;
    			len[clone]=len[p]+1;
    			for (int i=0;i<26;i++) {
    				nxt[clone][i]=nxt[q][i];
    			}
    			link[clone]=link[q];
    			while (p&&nxt[p][c-'a']==q) {
    				nxt[p][c-'a']=clone;
    				p=link[p];
    			}
    			link[q]=link[cur]=clone;
    		}
    	}
    	lst=cur;
    }
    const int M=maxn*40;
    int c[M],id[M],lson[M],rson[M],tol,T[maxn<<1];
    int up (int u,int l,int r,int p,int v) {
    	if (!u) u=++tol;
    	if (l==r) {
    		c[u]+=v;
    		id[u]=l;
    		return u;
    	}
    	int mid=(l+r)>>1;
    	if (p<=mid) lson[u]=up(lson[u],l,mid,p,v);
    	if (p>mid) rson[u]=up(rson[u],mid+1,r,p,v);
    	c[u]=max(c[lson[u]],c[rson[u]]);
    	if (c[rson[u]]==c[u]) id[u]=id[rson[u]];
    	if (c[lson[u]]==c[u]) id[u]=id[lson[u]];
    	return u;
    }
    int merge (int x,int y,int l,int r) {
    	if (!x||!y) return x+y;
    	int u=++tol;
    	if (l==r) {
    		c[u]=c[x]+c[y];
    		id[u]=l;
    		return u;
    	}
    	int mid=(l+r)>>1;
    	lson[u]=merge(lson[x],lson[y],l,mid);
    	rson[u]=merge(rson[x],rson[y],mid+1,r);
    	c[u]=max(c[lson[u]],c[rson[u]]);
    	if (c[rson[u]]==c[u]) id[u]=id[rson[u]];
    	if (c[lson[u]]==c[u]) id[u]=id[lson[u]];
    	return u;
    }
    pair<int,int> query (int u,int l,int r,int L,int R) {
    	//printf("%d %d %d %d %d\n",u,l,r,L,R);
    	if (L>R) return make_pair(0,0);
    	if (l>=L&&r<=R) {
    		return make_pair(c[u],id[u]);
    	}
    	int mid=(l+r)>>1;
    	pair<int,int> ans=make_pair(0,0);
    	if (L<=mid) ans=query(lson[u],l,mid,L,R);
    	if (R>mid) {
    		pair<int,int> tt=query(rson[u],mid+1,r,L,R);
    		if (tt.first>ans.first) {
    			ans.first=tt.first;
    			ans.second=tt.second;
    		}
    	}
    	return ans;
    }
    vector<int> g[maxn<<1];
    int father[25][maxn<<1];
    void dfs (int u) {
    	for (int i=1;i<=20;i++) father[i][u]=father[i-1][father[i-1][u]];
    	for (int v:g[u]) {
    		father[0][v]=u;
    		dfs(v);
    		T[u]=merge(T[u],T[v],1,m);
    	}
    }
    int ed[maxn];
    int main () {
    	ios::sync_with_stdio(false);
    	cin>>s;
    	for (int i=0;i<s.size();i++) {
    		sam_extend(s[i]);
    		ed[i+1]=lst;
    	}
    	cin>>m;
    	for (int i=1;i<=m;i++) {
    		cin>>t[i];
    		lst=1;
    		for (char ch:t[i]) sam_extend(ch),T[lst]=up(T[lst],1,m,i,1);
    	}
    	for (int i=2;i<=tot;i++) g[link[i]].push_back(i);
    	dfs(1);
    	cin>>q;
    	while (q--) {
    		int A,B,C,D;
    		cin>>C>>D>>A>>B;
    		int u=ed[B];
    		for (int i=20;i>=0;i--) {
    			if (father[i][u]&&len[father[i][u]]>=B-A+1) {
    				u=father[i][u];
    			}
    		}
    		pair<int,int> ans=query(T[u],1,m,C,D);
    		if (ans.first==0) {
    			ans={0,C};	
    		}
    		cout<<ans.second<<" "<<ans.first<<'\n';
    	}
    }
    
  • 相关阅读:
    洛谷P2805 植物大战僵尸
    洛谷P4307 球队收益
    bzoj4842 Delight for a Cat
    洛谷P2053 修车
    bzoj2561 最小生成树
    bzoj3114 LCM Pair Sum
    洛谷P4486 Kakuro
    bzoj3698 XWW的难题
    关于oracle数据库
    toString方法的用法
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15511022.html
Copyright © 2011-2022 走看看