BZOJ1396: 识别子串

题解:很裸的后缀数组 利用h函数的特性用线段树维护等差数列即可

#include <bits/stdc++.h>
const int MAXN=1e5+10;
const int inf=1e9+10;
using namespace std;
int txt[MAXN],sa[MAXN],t1[MAXN],t2[MAXN],rank1[MAXN],rank2[MAXN],td[MAXN];
bool cmp(int f[],int e,int w,int k){return f[e]==f[w]&&f[e+k]==f[w+k];}
void Sa(char s[]){
    int m=550;int len=strlen(s);
    int *rank1=t1;int *td=t2;
    for(int i=0;i<m;i++)txt[i]=0;
    for(int i=0;i<len;i++)rank1[i]=s[i],txt[s[i]]++;
    for(int i=1;i<m;i++)txt[i]+=txt[i-1];
    for(int i=len-1;i>=0;i--)sa[--txt[s[i]]]=i;
    for(int k=1;k<=len;k*=2){
	int p=0;
	for(int i=len-k;i<len;i++)td[p++]=i;
	for(int i=0;i<len;i++)if(sa[i]>=k)td[p++]=sa[i]-k;
	for(int i=0;i<m;i++)txt[i]=0;
	for(int i=0;i<len;i++)txt[rank1[i]]++;
	for(int i=1;i<m;i++)txt[i]+=txt[i-1];
	for(int i=len-1;i>=0;i--)sa[--txt[rank1[td[i]]]]=td[i];
	swap(rank1,td);
	rank1[sa[0]]=0;
	p=1;
	for(int i=1;i<len;i++)rank1[sa[i]]=cmp(td,sa[i],sa[i-1],k)?p-1:p++;
	if(p>=len)return ;
	m=p;
    }
    return ;
}
int H[MAXN],h[MAXN];
void hh(char s[]){
    int len=strlen(s);
    for(int i=0;i<len;i++)rank2[sa[i]]=i;
    memset(H,0,sizeof(H));
    for(int i=0;i<len;i++){
	if(rank2[i]==0)continue;
	int t=sa[rank2[i]-1];int w=i;int k;
	if(i==0||H[i-1]<=1)k=0;
	else k=H[i-1]-1,t+=k,w+=k;
	while(w<len&&t<len){
	    if(s[w]==s[t])k++;
	    else break;
	    t++;w++;
	}
	H[i]=k;h[rank2[i]]=k;
    }
}
int flag[MAXN<<2],flag1[MAXN<<2],ans[MAXN];
void push(int x,int l,int r){
    //flag[x<<1]=min(flag[x<<1],flag[x]);
    //flag[x<<1|1]=min(flag[x<<1|1],flag[x]);
    //flag[x]=inf;
    if(flag[x]!=inf){
	flag[x<<1]=min(flag[x],flag[x<<1]);
	flag[x<<1|1]=min(flag[x],flag[x<<1|1]);
	flag[x]=inf;
    }
    if(flag1[x]!=inf){
	flag1[x<<1]=min(flag1[x<<1],flag1[x]);
	int mid=(l+r)>>1;
	flag1[x<<1|1]=min(flag1[x<<1|1],flag1[x]+(mid+1-l));
	flag1[x]=inf;
    }
}
void built(int rt,int l,int r){
    flag[rt]=inf;flag1[rt]=inf;
    if(l==r)return ;
    int mid=(l+r)>>1;
    built(rt<<1,l,mid);
    built(rt<<1|1,mid+1,r);
}
void update1(int rt,int l,int r,int ql,int qr,int vul){
    if(ql<=l&&r<=qr){flag[rt]=min(flag[rt],vul);return ;}
    int mid=(l+r)>>1;
    push(rt,l,r);
    if(ql<=mid)update1(rt<<1,l,mid,ql,qr,vul);
    if(qr>mid)update1(rt<<1|1,mid+1,r,ql,qr,vul);
}
int start;
void update2(int rt,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr){flag1[rt]=min(flag1[rt],start);start+=(r+1-l);return ;}
    int mid=(l+r)>>1;
    push(rt,l,r);
    if(ql<=mid)update2(rt<<1,l,mid,ql,qr);
    if(qr>mid)update2(rt<<1|1,mid+1,r,ql,qr);
}
void querty(int rt,int l,int r){
    if(l==r){ans[l]=min(flag[rt],flag1[rt]);return ;}
    int mid=(l+r)>>1;
    push(rt,l,r);
    querty(rt<<1,l,mid);
    querty(rt<<1|1,mid+1,r);
}
char str[MAXN];
int main(){
    scanf(" %s",str);
    int len=strlen(str);str[len++]='$';
 //   cout<<str<<endl;
    Sa(str);hh(str);
  // for(int i=1;i<len;i++)cout<<h[i]<<" ";
   // cout<<endl;
   // cout<<"SA"<<endl;
   // for(int i=1;i<len;i++)cout<<sa[i]<<" ";
   // cout<<endl;
    //int len=strlen(str);
    h[len]=0;built(1,1,len-1);
    for(int i=1;i<len;i++){
	int len1=max(h[i],h[i+1]);
	// i+  //len1+1// i+len1
//	cout<<len1<<" "<<len<<endl;
	if(len-sa[i]-1<=len1)continue;
//	cout<<i<<" "<<len1<<endl;
	update1(1,1,len-1,sa[i]+1,sa[i]+len1+1,len1+1);
	start=len1+2;int lx=sa[i]+len1+2;
	if(lx<len)update2(1,1,len-1,lx,len-1);
    }
    querty(1,1,len-1);
    for(int i=1;i<len;i++){
	if(ans[i]==inf)printf("%d\n",len-1);
	else printf("%d\n",ans[i]);
    }
    return 0;
}

 

1396: 识别子串

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 602  Solved: 383
[Submit][Status][Discuss]

Description

Input

一行,一个由小写字母组成的字符串S,长度不超过10^5

Output

L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长.

Sample Input

agoodcookcooksgoodfood

Sample Output

1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4

posted @ 2018-06-08 23:50  wang9897  阅读(148)  评论(0编辑  收藏  举报