zoukankan      html  css  js  c++  java
  • [BZOJ 2865]字符串识别(后缀数组+线段树)

    [BZOJ 2865]字符串识别(后缀数组+线段树)(或后缀自动机+线段树)

    题面

    给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件:
    1、i≤K≤j。
    2、子串T只在S中出现过一次。

    现在,给定S,XX希望知道对于S的每一位,最短的识别子串长度是多少.

    (|S| leq 5 imes 10^5)

    分析

    SA做法

    引理:在字符串(S)的某个后缀(i)的所有前缀中,最短的识别子串的长度为(max(height[rank[i]],height[rank[i]+1])+1)

    证明: 感性理解,与后缀i字典序最接近的后缀,LCP长度一定更大。那么把这两个height取max,就得到了最长的LCP,再+1一定不会有重复的子串(否则height会更大).所以这一定是最短的识别子串

    (len=max(height[rank[i]],height[rank[i]+1])+1),

    那么这个识别子串对于([i,i+len-1])的这几位的答案,一定有len的贡献,于是把([i,i+len-1])的答案和len取min

    并且,对于(forall j in [i+len,n]),子串(s[i,j])一定是识别子串。因为若(s[i,j])重复出现,那(s[i,i+len-1])也一定重复出现,与引理矛盾。那么([i+len,n])上的位置(j)的答案与(j-i+1)取min

    第一种情况很好解决,用标记永久化的线段树区间修改即可。查询的时候把叶子节点到根上的所有标记取min即为答案。

    第二种情况同理,只是我们只维护(-i+1)的最小值,查询第(j)个位置的时候先同情况一在线段树上查询,然后把结果加上(j)即可。

    这样用两棵线段树维护两种情况的答案即可。时间复杂度(O(n log n))


    SAM做法

    对于后缀自动机上的一个节点(x),它对应的子串中最短的一个的长度应该是(len(link(x))+1).记right集合中的最大值是(maxpos(x)).
    (r=maxpos(x),l=maxpos(x)-(len(link(x))+1)+1).考虑我们求本质不同的子串个数的方法,(x)中对应的(len(link(x))+1)这个子串一定在其他位置没有出现过,那么区间([l,r])的最短识别子串长度就可能是(link(x)+1=r-l+1).我们把区间([l,r])的答案和(link(x)+1)取min
    显然一个识别子串加上一些字符后仍然也是识别子串,那么对于(p in [1,l-1]),它的最短识别子串长度可能是(r-p+1),同样取min即可。
    线段树维护类似上述方法。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring> 
    #define INF 0x3f3f3f3f
    #define maxn 500000
    #define maxs 128 
    using namespace std;
    inline void qprint(int x){
    	if(x<0){
    		putchar('-');
    		qprint(-x);
    	}else if(x==0){
    		putchar('0');
    		return;
    	}else{
    		if(x>=10) qprint(x/10);
    		putchar('0'+x%10);
    	}
    }
    void rsort(int *ans,int *fi,int *se,int n,int m){
    	static int buck[maxn+5];
    	for(int i=0;i<=m;i++) buck[i]=0;
    	for(int i=1;i<=n;i++) buck[fi[i]]++;
    	for(int i=1;i<=m;i++) buck[i]+=buck[i-1];
    	for(int i=n;i>=1;i--) ans[buck[fi[se[i]]]--]=se[i];
    }
    int sa[maxn+5],rk[maxn+5],height[maxn+5];
    void suffix_sort(char *s,int n,int m){
    	static int se[maxn+5];
    	for(int i=1;i<=n;i++){
    		rk[i]=s[i];
    		se[i]=i;
    	}
    	rsort(sa,rk,se,n,m);
    	for(int k=1;k<=n;k*=2){
    		int p=0;
    		for(int i=n-k+1;i<=n;i++) se[++p]=i;
    		for(int i=1;i<=n;i++) if(sa[i]>k) se[++p]=sa[i]-k;
    		rsort(sa,rk,se,n,m);
    		swap(rk,se);
    		rk[sa[1]]=1;
    		p=1;
    		for(int i=2;i<=n;i++){
    			if(se[sa[i-1]]==se[sa[i]]&&se[sa[i-1]+k]==se[sa[i]+k]) rk[sa[i]]=p;
    			else rk[sa[i]]=++p;
    		}
    		if(p==n) break;
    		m=p;
    	}
    }
    void get_height(char *s,int n,int m){
    	suffix_sort(s,n,m);
    	for(int i=1;i<=n;i++) rk[sa[i]]=i;
    	int k=0;
    	for(int i=1;i<=n;i++){
    		if(k) k--;
    		int j=sa[rk[i]-1];
    		while(s[i+k]==s[j+k]) k++;
    		height[rk[i]]=k;
    	}
    }
    
    struct segment_tree{
    	struct node{
    		int l;
    		int r;
    		int mark;//永久化标记	
    	}tree[maxn*4+5];
    	void build(int l,int r,int pos){
    		tree[pos].l=l;
    		tree[pos].r=r;
    		tree[pos].mark=INF;
    		if(l==r) return;
    		int mid=(l+r)>>1;
    		build(l,mid,pos<<1);
    		build(mid+1,r,pos<<1|1);
    	}
    	void update(int L,int R,int val,int pos){
    		if(L<=tree[pos].l&&R>=tree[pos].r){
    			tree[pos].mark=min(tree[pos].mark,val);
    			return;
    		}
    		//标记永久化,不用push_down
    		int mid=(tree[pos].l+tree[pos].r)>>1;
    		if(L<=mid) update(L,R,val,pos<<1);
    		if(R>mid) update(L,R,val,pos<<1|1); 
    	}
    	int query(int qpos,int pos){
    		if(tree[pos].l==tree[pos].r){
    			return tree[pos].mark;
    		}
    		int mid=(tree[pos].l+tree[pos].r)>>1;
    		if(qpos<=mid) return min(tree[pos].mark,query(qpos,pos<<1));
    		else return min(tree[pos].mark,query(qpos,pos<<1|1));
    	} 
    }T1,T2;
    
    int n;
    char s[maxn+5];
    int main(){
    #ifdef LOCAL
    //	freopen("1.in","r",stdin);
    //	freopen("1.ans","w",stdout);
    #endif
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	get_height(s,n,maxs);
    	T1.build(1,n,1);
    	T2.build(1,n,1);
    	for(int i=1;i<=n;i++){
    		int len=max(height[rk[i]],height[rk[i]+1])+1;
    		if(i+len-1<=n) T1.update(i,i+len-1,len,1);
    		if(i+len-1<n) T2.update(i+len,n,-i+1,1);//第j位的答案是j-i+1,不存储j,只存储min(-i+1) 
    	} 
    	for(int i=1;i<=n;i++){
    		qprint(min(T1.query(i,1),T2.query(i,1)+i));
    		putchar('
    '); 
    	}
    } 
    
    
    

    SAM:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue> 
    #define INF 0x3f3f3f3f
    #define maxn 500000
    #define maxc 26
    using namespace std;
    inline void qprint(int x){
    	if(x<0){
    		putchar('-');
    		qprint(-x);
    	}else if(x==0){
    		putchar('0');
    		return;
    	}else{
    		if(x>=10) qprint(x/10);
    		putchar('0'+x%10);
    	}
    }
    
    struct SAM{
    #define link(x) (t[x].link)
    #define len(x) (t[x].len)
    	struct node{
    		int ch[maxc];
    		int link;
    		int len;
    		int sz;//right集合大小 
    		int maxpos;//right集合中的最大值 
    	}t[maxn*2+5];
    	const int root=1;
    	int ptr=1;
    	int last=root;
    	void extend(char ch,int pos){
    		int c=ch-'a';
    		int p=last,cur=++ptr;
    		len(cur)=len(p)+1;
    		t[cur].sz=1;
    		t[cur].maxpos=pos;
    		while(p&&t[p].ch[c]==0){
    			t[p].ch[c]=cur;
    			p=link(p);
    		} 
    		if(p==0) link(cur)=root;
    		else{
    			int q=t[p].ch[c];
    			if(len(p)+1==len(q)) link(cur)=q;
    			else{
    				int clo=++ptr;
    				t[clo]=t[q];
    				len(clo)=len(p)+1;
    				t[clo].sz=0;
    				t[clo].maxpos=0;
    				link(q)=link(cur)=clo;
    				while(p&&t[p].ch[c]==q){
    					t[p].ch[c]=clo;
    					p=link(p);
    				}
    			}
    		}
    		last=cur;
    	} 
    	void topo_sort(){
    		static int in[maxn+5];
    		queue<int>q;
    		for(int i=1;i<=ptr;i++) in[link(i)]++;
    		for(int i=1;i<=ptr;i++) if(!in[i]) q.push(i);
    		while(!q.empty()){
    			int x=q.front();
    			q.pop();
    			t[link(x)].sz+=t[x].sz;
    			t[link(x)].maxpos=max(t[link(x)].maxpos,t[x].maxpos);
    			in[link(x)]--;
    			if(!in[link(x)]) q.push(link(x));
    		}
    	}
    	void solve(char *s){
    		int len=strlen(s+1);
    		for(int i=1;i<=len;i++) extend(s[i],i);
    		topo_sort();
    	}
    #undef len
    #undef link
    }S;
    
    struct segment_tree{
    	struct node{
    		int l;
    		int r;
    		int mark;
    	}tree[maxn*4+5];
    	void build(int l,int r,int pos){
    		tree[pos].l=l;
    		tree[pos].r=r;
    		tree[pos].mark=INF;
    		if(l==r) return;
    		int mid=(l+r)>>1;
    		build(l,mid,pos<<1);
    		build(mid+1,r,pos<<1|1);
    	}
    	void update(int L,int R,int val,int pos){
    		if(L<=tree[pos].l&&R>=tree[pos].r){
    			tree[pos].mark=min(tree[pos].mark,val);
    			return;
    		}
    		int mid=(tree[pos].l+tree[pos].r)>>1;
    		if(L<=mid) update(L,R,val,pos<<1);
    		if(R>mid) update(L,R,val,pos<<1|1); 
    	}
    	int query(int qpos,int pos){
    		if(tree[pos].l==tree[pos].r){
    			return tree[pos].mark;
    		}
    		int mid=(tree[pos].l+tree[pos].r)>>1;
    		if(qpos<=mid) return min(tree[pos].mark,query(qpos,pos<<1));
    		else return min(tree[pos].mark,query(qpos,pos<<1|1));
    	} 
    }T1,T2;
    
    int n;
    char s[maxn+5];
    int main(){
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	S.solve(s);
    	T1.build(1,n,1);
    	T2.build(1,n,1);
    	for(int i=1;i<=S.ptr;i++){
    		if(S.t[i].sz==1){
    			int r=S.t[i].maxpos,l=r-(S.t[S.t[i].link].len+1)+1;
    			T1.update(l,r,r-l+1,1);
    			T2.update(1,l-1,r+1,1);
    		}
    	}
    	for(int i=1;i<=n;i++){
    		qprint(min(T1.query(i,1),-i+T2.query(i,1)));
    		putchar('
    ');
    	}
    } 
    
    
    
  • 相关阅读:
    vue学习之router
    vue学习之组件
    xshell操作
    Webstorm快捷操作
    javascript判断节点是否在dom
    影子节点 shadowDOM
    虚拟节点操作——DocumentFragment
    理解浏览器的历史记录
    浏览器渲染
    web请求流程
  • 原文地址:https://www.cnblogs.com/birchtree/p/12246503.html
Copyright © 2011-2022 走看看