[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 \times 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('\n'); 
	}
} 


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('\n');
	}
} 


posted @ 2020-01-31 19:59  birchtree  阅读(254)  评论(0编辑  收藏  举报