• 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<i)的延伸[lj,i]

    对于这种情况,我们把所有区间按r排序,对于i,我们用二分找到最大的j使得rj<i,让后答案就是i-max(l[k])(k<=j)

    两种情况取较小就可以了

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

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #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.r<b.r; 
    }
    inline bool c2(Pr a,Pr b){ 
    	return a.r-a.l>b.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<R) cover(mid+1,r,x<<1|1,L,R,k);
    }
    int gCover(int l,int r,int x,int p){
    	if(l==r) return T[x];
    	if(T[x]){ T[x<<1]=T[x<<1|1]=T[x]; T[x]=0; }
    	int mid=l+r>>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<r;){
    			mid=l+r+1>>1;
    			if(R[mid].r<=i) l=mid;
    			else r=mid-1;
    		}
    		printf("%d
    ",min(A[i],i-M[l]+1));
    	}
    }


  • 相关阅读:
    数据库+mysql
    网络并发项目
    网络编程 IO模型
    并发编程之死锁、进程池、协程
    类的使用
    面向对象—多态,反射
    面向对象-三大特性
    继承与派生
    面向对象
    模块进阶(二)
  • 原文地址:https://www.cnblogs.com/Extended-Ash/p/7774417.html
Copyright © 2020-2023  润新知