Jzoj2921【NOI2012模拟题】字符串识别

题意:给一个字符串,对于每个位置i,求出最短满足的子串[l,r]使得i∈[l,r]且这个子串只出现一次,输出这个子串的长度

神(shui)题,AC后看了下别人的code发现全都是后缀数组(难看

可能是因为我太弱了所以只想到SAM的算法

做法:建立SAM,求出parent树,预处理倍增,让后就可以O(lg n)查询一个字串的出现次数了


接下来,我们对于每个l∈[1,n],我们求出一个最小的r使得[l,r]在整个串里面只出现一次

那么对于每个点i,ans[i]有两种情况:

1.i被一个最短的区间[lj,rj]覆盖,那么我们可以

把这些l,r区间按长度从大到小排序,并且用线段树区间覆盖

2.有可能是一个区间[lj,rj](rj

对于这种情况,我们把所有区间按r排序,对于i,我们用二分找到最大的j使得rj

两种情况取较小就可以了

代码很长很丑,跑的也很慢。。。。

#include
#include
#include
#define N 1000010
#define f F[0]
using namespace std;
struct Pr{ int l,r; } R[N];
int F[20][N],s[N][26],mx[N],sz[N],d[N],M[N],n;
int v[N],r[N],m=1,cnt=1,last=1,A[N],pos[N],T[N<<2]; 
char str[N];
inline bool c1(Pr a,Pr b){ 
	return a.r==b.r?a.l>b.l:a.rb.r-b.l;
}
inline int extend(char c,int& o){
	int p=last,np=last=o=++cnt,q,nq;
	c-='a'; mx[np]=mx[p]+1; sz[np]=1;
	for(;p&&!s[p][c];p=f[p]) s[p][c]=np;
	if(!p) return f[np]=1;
	q=s[p][c];
	if(mx[p]+1==mx[q]) f[np]=q;
	else {
		nq=++cnt;
		mx[nq]=mx[p]+1;
		f[nq]=f[q]; f[q]=f[np]=nq;
		memcpy(s[nq],s[q],26<<2);
		for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;
	}
}
inline void build(){
	for(int i=1;i<=cnt;++i) ++v[mx[i]];
	for(int i=1;i<=n;++i) v[i]+=v[i-1];
	for(int i=cnt;i;--i) r[v[mx[i]]--]=i;
	for(int p,i=cnt;i;--i) sz[f[r[i]]]+=sz[r[i]];
	for(int p,i=1;i<=cnt;++i){
		p=r[i]; d[p]=d[f[p]]+1;
		for(int j=1;j<20;++j) F[j][i]=F[j-1][F[j-1][i]];
	}
}
int gSz(int l,int r){
	if(r>n) return 1;
	int x=pos[r];
	for(int p,i=19;~i&&sz[x]<=1;--i){
		p=F[i][x];
		mx[p]>=r-l+1?x=p:0;
	}
	return sz[x];
}
void cover(int l,int r,int x,int L,int R,int k){
	if(L<=l && r<=R){ T[x]=k; return; }
	if(T[x]){ T[x<<1]=T[x<<1|1]=T[x]; T[x]=0; }
	int mid=l+r>>1;
	if(L<=mid) cover(l,mid,x<<1,L,R,k);
	if(mid>1;
	if(p<=mid) return gCover(l,mid,x<<1,p);
	  else return gCover(mid+1,r,x<<1|1,p);
}
int main(){
	scanf("%s",str+1); n=strlen(str+1); 
	for(int i=1;i<=n;++i) extend(str[i],pos[i]);
	build(); int l=1,r=1;
	for(;l<=n;){
		for(;r<=n&&gSz(l,r)>1;++r);
		R[l]=(Pr){l,r};
		if(++l>r) ++r;
	}
	sort(R+1,R+1+n,c2); T[1]=n;
	for(int i=1;i<=n;++i) 
		if(R[i].r<=n) cover(1,n,1,R[i].l,R[i].r,R[i].r-R[i].l+1);
	for(int i=1;i<=n;++i) 
		A[i]=gCover(1,n,1,i);
	sort(R+1,R+1+n,c1); M[0]=-n;
	for(int i=1;i<=n;++i) M[i]=max(M[i-1],R[i].l);
	for(int i=1;i<=n;++i){
		l=0; r=n;
		for(int mid;l>1;
			if(R[mid].r<=i) l=mid;
			else r=mid-1;
		}
		printf("%d\n",min(A[i],i-M[l]+1));
	}
}


你可能感兴趣的:(OI,数据结构,----线段树,字符串,----后缀自动机,求解策略,----二分/三分,Jzoj,后缀自动机,字符串,后缀,后缀数组)