zoukankan      html  css  js  c++  java
  • ●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
    ",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;
    }
    

      

  • 相关阅读:
    获取字符串中指定字符间的字符串
    删除一个xml
    读取文件夹下所有文件名,饼写入xml
    在现有xml增加一个新的节点
    某一时间执行某方法c# 写在global里
    Ubuntu下安装Adobe Flash Player
    Josephus(约瑟夫环)
    html5综合属性图表
    第一步
    框架学习的个人见解
  • 原文地址:https://www.cnblogs.com/zj75211/p/8541837.html
Copyright © 2011-2022 走看看