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 MBSubmit: 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