zoukankan      html  css  js  c++  java
  • [HEOI2016/TJOI2016]字符串(后缀自动机,可持久化线段树,线段树合并,二分答案)

    [HEOI2016/TJOI2016]字符串

    给出一个长度为\(n\)的字符串\(s\),和\(m\)个问题。

    每个问题均有\(a,b,c,d\)四个参数,问你子串\(s[a,b]\)的所有子串和\(s[c,d]\)的最长公共前缀的长度的最大值是多少?

    做法:

    二分一个答案\(mid\)

    现在我们要判断\(s[c,c+mid-1]\)是否在\(s[a,b]\)出现过。

    首先找到\(s[c,c+mid-1]\)所在的状态:

    建出link树,从\(s[1,c+mid-1]\)的节点倍增向上跳到最后一个\(len>=mid\)的节点。根据后缀自动机存储所有子串的性质,这个节点表示的子串一定有一个长度为\(len\)

    记这个节点为\(now\)

    现在我们要判断\(now\)\(endpos\)集合中是否含有\([a+mid-1,b]\)中的某个数,我们给每个节点开一个权值线段树用来维护该节点的\(endpos\)位置,然后自底向上合并线段树即可。这里需要可持久化的合并,因为每个线段树要被重复使用。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=5e5+10;
    const int M=maxn*40;
    int len[maxn],link[maxn],nxt[maxn][26],tot=1,lst=1;
    string s;
    void sam_extend (char c) {
    	int cur=++tot;
    	len[cur]=len[lst]+1;
    	int p=lst;
    	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;
    }
    vector<int> g[maxn];
    int n,m,p[maxn],father[25][maxn],T[maxn],lson[M],rson[M],c[M],tol;
    int up (int u,int l,int r,int p,int v) {
    	if (!u) u=++tol;
    	if (l==r) {
    		c[u]+=v;
    		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]=c[lson[u]]+c[rson[u]];
    	return u;
    }
    int merge (int x,int y,int l,int r) {
    	if (!x||!y) return x+y;
    	int u=++tol;
    	c[u]=c[x]+c[y];
    	if (l==r) 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);
    	return u;
    }
    int query (int u,int l,int r,int L,int R) {
    	if (L>R) return 0;
    	if (l>=L&&r<=R) return c[u];
    	int mid=(l+r)>>1;
    	int ans=0;
    	if (L<=mid) ans+=query(lson[u],l,mid,L,R);
    	if (R>mid) ans+=query(rson[u],mid+1,r,L,R);
    	return ans;
    }
    void dfs (int u) {
    	for (int v:g[u]) {
    		father[0][v]=u;
    		dfs(v);
    		T[u]=merge(T[u],T[v],1,n);
    	}
    } 
    bool check (int mid,int a,int b,int c,int d) {
    	int u=p[c+mid-1];
    	for (int i=20;i>=0;i--) {
    		if (father[i][u]&&len[father[i][u]]>=mid) u=father[i][u];
    	}
    	return query(T[u],1,n,a+mid-1,b)>0;
    }
    int main () {
    	ios::sync_with_stdio(false);
    	cin>>n>>m;
    	cin>>s;
    	p[0]=1;
    	for (int i=1;i<=n;i++) {
    		sam_extend(s[i-1]);
    		p[i]=lst;
    		T[lst]=up(T[lst],1,n,i,1);
    	} 
    	for (int i=2;i<=tot;i++) g[link[i]].push_back(i);
    	dfs(1);
    	for (int i=1;i<=20;i++) for (int j=1;j<=tot;j++) father[i][j]=father[i-1][father[i-1][j]];
    	while (m--) {
    		int a,b,c,d;
    		cin>>a>>b>>c>>d;
    		int l=0,r=min(b-a+1,d-c+1);
    		int ans=0;
    		while (l<=r) {
    			int mid=(l+r)>>1;
    			if (check(mid,a,b,c,d)) {
    				ans=mid;
    				l=mid+1;
    			}
    			else {
    				r=mid-1;
    			}
    		} 
    		cout<<ans<<'\n';
    	}
    }
    
  • 相关阅读:
    Offer快到碗里来,囊中之物-CAS
    SQL 两表一对多关联,主表某字段保存所有关联的id
    No Feign Client for loadBalancing defined
    Clean Code读书笔记 3--类
    Clean Code读书笔记(2)---函数
    Clean Code读书笔记(1)---有意义的命名
    [CF1354D] Multiset
    [CF1365E] Maximum Subsequence Value
    [CF1358D] The Best Vacation
    [CF463C] Gargari and Bishops
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15508632.html
Copyright © 2011-2022 走看看