zoukankan      html  css  js  c++  java
  • bzoj 1396 识别子串 后缀树+线段树

    题目大意

    给定一个长度(le100000)的字符串
    求每一个位置的最短识别子串
    对于位置(x),能识别子串(s[i...j])的条件是
    1.(ile x le j)
    2.(s[i...j])在原串中只出现了一次

    分析

    从第二个条件入手
    仅出现一次子串就是后缀树上(|right|=1)的子串
    考虑贡献
    该后缀左端点在(left)
    长度范围([L,R])
    如图
    对于(A)部分贡献的最短串长度为(L)
    对于(B)部分贡献是等差数列(i-lef+1),提出(1-lef)
    对上面分两棵线段树维护即可

    solution

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    const int M=200007;
    const int INF=1e9+7;
    
    struct edge{int y,nxt;};
    struct vec{
    	int g[M],te;
    	edge e[M];
    	vec(){memset(g,0,sizeof(g));te=0;}
    	void clear(){memset(g,0,sizeof(g));te=0;}
    	inline void push(int x,int y){e[++te].y=y;e[te].nxt=g[x];g[x]=te;}
    	inline int& operator () (int x){return g[x];}
    	inline edge& operator [] (int x){return e[x];}
    }go;
    
    char s[M];
    int n;
    int ch[M][26];
    int stp[M],fa[M];
    int lef[M];
    int last,tot;
    int ans[M];
    
    struct Seg{
    	int tag;
    	Seg(){tag=INF;}
    }seg1[262147],seg2[262147];
    
    int newnode(int ss){
    	stp[++tot]=ss;
    	return tot;
    }
    
    int ext(int p,int q,int d){
    	int nq=newnode(stp[p]+1);
    	fa[nq]=fa[q]; fa[q]=nq;
    	memcpy(ch[nq],ch[q],sizeof(ch[q]));
    	for(;p&&ch[p][d]==q;p=fa[p]) ch[p][d]=nq;
    	return nq;
    }
    
    int sam(int p,int d){
    	int np=ch[p][d];
    	if(np) return (stp[p]+1==stp[np]) ? np : ext(p,np,d);
    	
    	np=newnode(stp[p]+1);
    	for(;p&&!ch[p][d];p=fa[p]) ch[p][d]=np;
    	if(!p) fa[np]=1;
    	else{
    		int q=ch[p][d];
    		fa[np]= (stp[p]+1==stp[q]) ? q : ext(p,q,d);
    	}
    	return np;
    }
    
    void mdf(Seg *S,int x,int l,int r,int tl,int tr,int d){
    	if(S[x].tag<=d) return;
    	if(tl<=l&&r<=tr) {S[x].tag=d;return;}
    	int mid=l+r>>1;
    	if(tl<=mid) mdf(S,x<<1,l,mid,tl,tr,d);
    	if(mid<tr) mdf(S,x<<1|1,mid+1,r,tl,tr,d);
    }
    
    void get(Seg *S,int x,int l,int r,int nw,int kd){
    	nw=min(nw,S[x].tag);
    	if(l==r){
    		if(kd==0) ans[l]=min(ans[l],nw);
    		else ans[l]=min(ans[l],l+nw);
    		return;
    	}
    	int mid=l+r>>1;
    	get(S,x<<1,l,mid,nw,kd);
    	get(S,x<<1|1,mid+1,r,nw,kd);
    }
    
    void dfs(int x){
    	int p,y;
    	for(p=go(x);p;p=go[p].nxt){
    		y=go[p].y;
    		dfs(y);
    		if(lef[y]){
    			if(lef[x]==0) lef[x]=lef[y];
    			else if(lef[x]>0) lef[x]=-1;
    		}
    	}
    	if(lef[x]>0){//if only one lef
    		int L=stp[fa[x]]+1;
    		int R=stp[x];
    		mdf(seg1,1,1,n,lef[x],lef[x]+L-1, L);
    		if(L!=R) mdf(seg2,1,1,n,lef[x]+L,lef[x]+R-1, 1-lef[x]);
    	}
    }
    
    int main(){
    	
    	int i;
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	
    	last=tot=1;
    	for(i=n;i>0;i--){
    		last=sam(last,s[i]-'a');
    		lef[last]=i;
    	}
    	
    	for(i=2;i<=tot;i++) go.push(fa[i],i);
    	dfs(1);
    	
    	for(i=1;i<=n;i++) ans[i]=INF;
    	get(seg1,1,1,n,INF,0);
    	get(seg2,1,1,n,INF,1);
    	for(i=1;i<=n;i++) printf("%d
    ",ans[i]);
    	
    	return 0;
    }
    
  • 相关阅读:
    20200722T1 【NOIP2015模拟10.29A组】三色树
    【NOIP2015模拟10.29B组】抓知了
    20200721T2 【NOIP2015模拟10.22】最大子矩阵
    20200721T1 【NOIP2015模拟10.22】矩形
    20200720T4 五子棋
    [JZOJ3809] 设备塔
    注册了!
    Python之元组和集合
    Python中列表详解
    python 字符串
  • 原文地址:https://www.cnblogs.com/acha/p/6592853.html
Copyright © 2011-2022 走看看