zoukankan      html  css  js  c++  java
  • BZOJ bzoj1396 识别子串

    题面: bzoj1396

    题解:

    先建出SAM,并计算right集合大小。显然符合条件的点的right集合大小为1.

    对于每个right集合为1的状态显然可以算出这些状态的pos以及maxlen和minlen(fa的len+1)。

    然后对于在pos和pos-minlen+1区间内的字符显然必须选长为minlen的一段区间。因此我们搞一棵线段树维护这些minlen,即对于在pos和pos-minlen+1区间内的字符在线段树上和minlen取min

    对于另外的在pos-maxlen+1到pos-minlen+1的区间内,这些字符的区间长度都为(pos-pos[i])(第二个pos[i]是每个字符的位置,第一个是该状态的pos),然后发现所有的这种情况都是(pos)减一个值,那么我们为了让其min,就另开一棵线段树维护每次pos的最小值。

    最后统计答案即可。

    #include<bits/stdc++.h>
    #define ls (now<<1)
    #define rs (now<<1|1)
    
    using namespace std;
    
    namespace Tzh{
    	
    	const int maxn=4e5+10;
    	const int inf=INT_MAX;
    	int tot=1,last=1,c[maxn],a[maxn];
    	string S;
    	
    	struct Suffix_AutoMaton{
    		int cnt,link,pos,len,ch[26];	
    	}sam[maxn<<1];
    	
    	struct segment_tree{
    		
    		int ans[maxn]; 
    		
    		struct Tr{
    			int lt,rt,tag;	
    		}tree[maxn<<2];
    		
    		void build(int now,int lt,int rt){
    			if(lt>rt) return ; tree[now].tag=inf;
    			tree[now].lt=lt,tree[now].rt=rt;
    			if(lt==rt) return ;
    			int mid=lt+rt>>1;
    			build(ls,lt,mid),build(rs,mid+1,rt);	
    		}
    		
    		void change(int now,int lt,int rt,int w){
    			if(tree[now].rt<lt||tree[now].lt>rt) return;
    			if(tree[now].lt>=lt&&tree[now].rt<=rt)
    				tree[now].tag=min(tree[now].tag,w);
    			else change(ls,lt,rt,w),change(rs,lt,rt,w);
    		}
    		
    		void dfs(int now){
    			if(tree[now].lt==tree[now].rt){
    				ans[tree[now].lt]=tree[now].tag; return;
    			}
    			tree[ls].tag=min(tree[ls].tag,tree[now].tag);
    			tree[rs].tag=min(tree[rs].tag,tree[now].tag);
    			dfs(ls),dfs(rs);	
    		}
    		
    	}seg1,seg2;
    	
    	void build(int x){ int cur=++tot,p=last;	
    		sam[cur].len=sam[last].len+1; sam[cur].cnt=1;
    		sam[cur].pos=sam[cur].len-1; last=cur;
    		for(;p&&!sam[p].ch[x];p=sam[p].link) sam[p].ch[x]=cur;
    		if(!p) sam[cur].link=1;
    		else{ int q=sam[p].ch[x];
    			if(sam[q].len==sam[p].len+1) sam[cur].link=q;
    			else{ int clone=++tot;
    				sam[clone]=sam[q]; sam[clone].cnt=0; 
    				sam[clone].len=sam[p].len+1;
    				for(;p&&sam[p].ch[x]==q;p=sam[p].link)
    					sam[p].ch[x]=clone;
    				sam[q].link=sam[cur].link=clone;
    			}
    		}
    	}
    	
    	void cal(){
    		for(int i=1;i<=tot;i++) c[sam[i].len]++;
    		for(int i=1;i<=tot;i++) c[i]+=c[i-1];
    		for(int i=1;i<=tot;i++) a[c[sam[i].len]--]=i;
    		for(int i=tot;i;i--){ int p=a[i];
    			sam[sam[p].link].cnt+=sam[p].cnt;
    		}
    	}
    	
    	void work(){
    		cin>>S;
    		for(int i=0;i<S.size();i++) build(S[i]-'a');
    		cal();seg1.build(1,0,S.size()),seg2.build(1,0,S.size());
    		for(int i=2;i<=tot;i++) if(sam[i].cnt==1){ int minlen=sam[sam[i].link].len+1;
    			seg1.change(1,sam[i].pos-minlen+1,sam[i].pos,minlen);
    			seg2.change(1,sam[i].pos-sam[i].len+1,sam[i].pos-minlen+1,sam[i].pos);
    		}
    		seg1.dfs(1),seg2.dfs(1);
    		for(int i=0;i<S.size();i++)
    			printf("%d
    ",min(seg1.ans[i],seg2.ans[i]-i+1));	
    		return ;
    	}
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("1396.in","r",stdin);
    	freopen("1396.out","w",stdout);
    #endif
    	ios::sync_with_stdio(false);
    	Tzh::work();
    	return 0;	
    }
    
  • 相关阅读:
    记录
    集合
    数据库一键退出脚本
    修改NLS_DATE_FORMAT的四种方式
    触发器
    (转)rlwrap真是一个好东西
    Windows常用技巧集锦
    UTL_FILE
    redis入门(03)redis的配置
    服务网关
  • 原文地址:https://www.cnblogs.com/tang666/p/9209513.html
Copyright © 2011-2022 走看看