●BZOJ 1396 识别子串

Posted *ZJ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了●BZOJ 1396 识别子串相关的知识,希望对你有一定的参考价值。

题链:

http://www.joyoi.cn/problem/tyvj-2301(非权限OI患者,苟且在joyoi。。。)
题解:

后缀自动机,线段树

先对原串建立后缀自动机,不难发现,
会影响答案是那些right集合大小恰好为1的状态。

考虑这些状态是如何影响答案的。
对于一个right集合大小为1的状态s,
令其允许的最大长度为maxs[s],其允许的最小长度为maxs[parent[s]]+1,其right集合里唯一的元素是minr(这里minr表示该状态对应的串在该位置结束)
我们可以得到对应的l=minr-maxs[s]+1,r=minr-maxs[parent[s]],即该状态对应的子串集就是S[l~r,minr]。
显然对于S[r~minr]这些字符,该状态可以给他们贡献一个maxs[parent[s]]+1的答案(对应着只出现过一次的子串S[r,minr])。
而对于S[l~r]这些字符,该状态可以给他们贡献的答案并不是一个相同的值,但是有一个共同点,
就是贡献给第i个字符的答案对应的子串是S[i,minr],即他们有着相同的结尾位置。
考虑到上面的两种贡献都是区间贡献,所以用线段树分别维护两种贡献即可。
(线段树的实现可以考虑先给线段树区间打上永久化标记,最后再通过一遍dfs得出每个叶子节点的最优答案。)


代码:

 

#include<bits/stdc++.h>
#define MAXN 100005
#define INF 0x3f3f3f3f
using namespace std;
struct SGT{
	int rt,size;
	int ls[MAXN*2],rs[MAXN*2],lazy[2][MAXN*2];
	void Build(int &u,int l,int r){
		u=++size;
		lazy[0][u]=lazy[1][u]=INF;
		if(l==r) return;
		int mid=(l+r)/2;
		Build(ls[u],l,mid);
		Build(rs[u],mid+1,r);
	}
	void Modify(int u,int l,int r,int al,int ar,int val,int k){
		if(al<=l&&r<=ar) return (void)(lazy[k][u]=min(lazy[k][u],val));
		int mid=(l+r)/2;
		if(al<=mid) Modify(ls[u],l,mid,al,ar,val,k);
		if(mid<ar) Modify(rs[u],mid+1,r,al,ar,val,k);
	}
	void Answer(int u,int l,int r,int minlazy0,int minlazy1){
		minlazy0=min(minlazy0,lazy[0][u]);
		minlazy1=min(minlazy1,lazy[1][u]);
		if(l==r){
			printf("%d\n",min(minlazy0,minlazy1-l+1));
			return;
		}
		int mid=(l+r)/2;
		Answer(ls[u],l,mid,minlazy0,minlazy1);
		Answer(rs[u],mid+1,r,minlazy0,minlazy1);
	}
}DT;
struct SAM{
	int size;
	int maxs[MAXN*3],trans[MAXN*3][26],parent[MAXN*3],minr[MAXN*3],right[MAXN*3];
	int Newnode(int a,int b){
		++size; maxs[size]=a; minr[size]=INF;
		memcpy(trans[size],trans[b],sizeof(trans[b]));
		return size;
	}
	int Extend(int last,int x){
		static int p,np,q,nq;
		p=last; np=Newnode(maxs[p]+1,0);
		for(;p&&!trans[p][x];p=parent[p]) trans[p][x]=np;
		if(!p) parent[np]=1;
		else{
			q=trans[p][x];
			if(maxs[p]+1!=maxs[q]){
				nq=Newnode(maxs[p]+1,q);
				parent[nq]=parent[q];
				parent[q]=parent[np]=nq;
				for(;p&&trans[p][x]==q;p=parent[p]) trans[p][x]=nq;
			}
			else parent[np]=q;
		}
		return np;
	}
	void Build(char *S){
		static int p=1,last,len,tmp[MAXN],order[MAXN*3];
		memset(trans[0],0,sizeof(trans[0]));
		size=0; last=Newnode(0,0); len=strlen(S);
		for(int i=0;i<len;i++) last=Extend(last,S[i]-‘a‘);
		for(int i=0;i<len;i++) p=trans[p][S[i]-‘a‘],minr[p]=i,right[p]=1;
		for(int i=1;i<=size;i++) tmp[maxs[i]]++;
		for(int i=1;i<=len;i++) tmp[i]+=tmp[i-1];
		for(int i=1;i<=size;i++) order[tmp[maxs[i]]--]=i;
		for(int i=size;i;i--){
			p=order[i];
			minr[parent[p]]=min(minr[parent[p]],minr[p]);
			right[parent[p]]+=right[p];
		}
	}
	void Solve(int len){
		for(int i=1,l,r;i<=size;i++) if(right[i]==1){
			l=minr[i]-maxs[i]+1; r=minr[i]-maxs[parent[i]];
			DT.Modify(DT.rt,0,len-1,r,minr[i],maxs[parent[i]]+1,0);
			DT.Modify(DT.rt,0,len-1,l,r,minr[i],1);
		}
	}
}SUF;
int main(){
	static char S[MAXN];
	scanf("%s",S);
	int len=strlen(S);
	DT.Build(DT.rt,0,len-1);
	SUF.Build(S);
	SUF.Solve(len);
	DT.Answer(DT.rt,0,len-1,INF,INF);
	return 0;
}

 

  

 

以上是关于●BZOJ 1396 识别子串的主要内容,如果未能解决你的问题,请参考以下文章

bzoj1396 识别子串

BZOJ 1396:识别子串 SA+树状数组+单调队列

bzoj1396: 识别子串

[BZOJ1396]识别子串 后缀自动机+线段树

●BZOJ 1396 识别子串

bzoj1396 识别子串