约定:以下字符串下标从1开始
对于字符串$s_{1}$和$s_{2}$,定义以下信息——
定义$s_{1}approx s_{2}$当且仅当$s_{1}[1,l]=s_{2}[1,l]$(其中$l=min(|s_{1}|,|s_{2}|)$)
定义$s_{1}ll s_{2}$当且仅当$s_{1}<s_{2}$且$s_{1} otapprox s_{2}$,$s_{1}gg s_{2}$当且仅当$s_{1}>s_{2}$且$s_{1} otapprox s_{2}$
结论:若$s=caab$,则$f(s) e |c|+|a|+1$(其中$a,b,c$为任意字符串,且$a,b$非空)
考虑取另外$|c|+1$和$|c|+2|a|+1$的位置,即求证$abcale aabc$或$abca<bcaa$
(前者取等号,因为当相等时,$f(s)$会取靠前的位置)
当$bcale abc$时,前者显然成立;当$bca>abc$时,后者显然成立
(另外,当$b$为空时此结论也成立,但本文中并不需要利用此性质)
$forall xin [1,i]$,$xin S_{i}$当且仅当其满足:
1.$forall 1le yle i,s[x,i] otll s[y,i]$
2.$forall 1le y<x且2x-yle i,s[y,i] otapprox s[x,i]$
(这个条件即等价于不存在$s[1,i]=caab$使得$f(s)=|c|+|a|+1$)
显然,有$f(s[1,i])in S_{i}$,下面考虑$|S_{i}|$的大小——
结论:$|S_{i}|sim o(log n)$
若$x,yin S_{i}$(不妨假设$x<y$),根据第一个性质,则有$s[x,i]approx s[y,i]$
若$2y-xle i$,即可以得到$s[x,y)=s[y,2y-x)$,与第2个性质矛盾
因此$2y-x>i$,也即$2|s[y,i]|le |s[x,i]|$,因此将$S_{i}$中的$x$写作$i-x+1$从小到大排列后,后一项大于等于前一项的两倍且值域为$[1,i]$,即有$|S_{i}|sim o(log n)$
下面,考虑如何求$S_{i}$,显然$S_{i}subseteq S_{i-1}cup {i}$,从小到大枚举$S_{i-1}cup {i}$中的元素$x$,并判定能否加入$S_{i}$中
关于这里,判定只需要考虑$S_{i}$中的元素(正确性显然),更具体的即如下做法——
初始$S_{i}$为空,若$S_{i}$为空则直接加入,否则令$y$为当前$S_{i}$中的最大值,考虑$s_{i}$和$s_{y+i-x}$的关系:
1.$s_{i}>s_{y+i-x}$,不加入$x$
2.$s_{i}<s_{y+i-x}$,令$S_{i}={x}$
3.$s_{i}=s_{y+i-x}$,若$2x-y>i$则加入$x$
初始为$S_{0}=empty$,综上即可求出$S_{i}$,且复杂度为$o(nlog n)$
最后,考虑如何求$f(s[1,i])$,根据$f(s[1,i])in S_{i}$,可以暴力枚举$S_{i}$中的元素并判定
下面,我们要快速判定$s[x,i]+s[1,x)$和$s[y,i]+s[1,y)$的大小关系(不妨假设$x<y$),由于$x,yin S_{i}$,因此有$s[x,i]approx s[y,i]$,也即$s[y,i]$是$s[x,i]$的前缀
换言之,即判定$s(i-(y-x),i]+s[1,x)$和$s[1,y)$的大小关系,考虑求lcp后判定第一个不同的字符,那么问题即求$s$某个后缀与$s$的lcp长度,有以下三种做法:
1.二分+哈希可以做到$o(log n)$,但总复杂度即变为$o(nlog^{2}n)$,无法通过
2.后缀数组,复杂度为$o(nlog n)$,常数太大,无法通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 3000005 4 int n,ans,rk[N<<1],a[N],b[N],c[N],tot[N],sa[N],h[N],v[N],vv[N],num[11]; 5 char s[N]; 6 void write(int x){ 7 while (x){ 8 num[++num[0]]=x%10; 9 x/=10; 10 } 11 for(;num[0];num[0]--)putchar(num[num[0]]+'0'); 12 } 13 int main(){ 14 scanf("%s",s+1); 15 n=strlen(s+1); 16 for(int i=1;i<=n;i++)rk[i]=s[i]-'a'+1; 17 int m=26; 18 for(int i=1;;i<<=1){ 19 memset(tot,0,sizeof(tot)); 20 for(int j=1;j<=n;j++)tot[rk[j+i]]++; 21 for(int j=1;j<=m;j++)tot[j]+=tot[j-1]; 22 for(int j=1;j<=n;j++)a[tot[rk[j+i]]--]=j; 23 memset(tot,0,sizeof(tot)); 24 for(int j=1;j<=n;j++)tot[rk[j]]++; 25 for(int j=2;j<=m;j++)tot[j]+=tot[j-1]; 26 for(int j=n;j;j--)b[tot[rk[a[j]]]--]=a[j]; 27 m=0; 28 for(int j=1;j<=n;j++){ 29 if ((j==1)||(rk[b[j]]!=rk[b[j-1]])||(rk[b[j]+i]!=rk[b[j-1]+i]))m++; 30 c[b[j]]=m; 31 } 32 memcpy(rk,c,sizeof(c)); 33 if (m==n)break; 34 } 35 for(int i=1;i<=n;i++)sa[rk[i]]=i; 36 for(int i=1,k=0;i<=n;i++,k-=(k>0)){ 37 while ((i+k<=n)&&(sa[rk[i]+1]+k<=n)&&(s[i+k]==s[sa[rk[i]+1]+k]))k++; 38 h[rk[i]]=k; 39 } 40 for(int i=rk[1]-2;i;i--)h[i]=min(h[i],h[i+1]); 41 for(int i=n;i>rk[1];i--)h[i]=h[i-1]; 42 h[rk[1]]=n; 43 for(int i=rk[1]+1;i<=n;i++)h[i]=min(h[i],h[i-1]); 44 memcpy(c,h,sizeof(c)); 45 for(int i=1;i<=n;i++)h[i]=c[rk[i]]; 46 for(int i=1;i<=n;i++){ 47 for(int j=0;j<=v[0];j++)vv[j]=v[j]; 48 vv[++vv[0]]=i; 49 v[0]=0; 50 for(int j=1;j<=vv[0];j++){ 51 if (!v[0])v[++v[0]]=vv[j]; 52 else{ 53 int x=vv[j],y=v[v[0]]; 54 if (s[i]<s[y+i-x])v[0]=0; 55 if ((s[i]<s[y+i-x])||(s[i]==s[y+i-x])&&(2*x-y>i))v[++v[0]]=x; 56 } 57 } 58 ans=v[1]; 59 for(int j=2;j<=v[0];j++){ 60 int x=ans,y=v[j],l=h[i-(y-x)+1]; 61 if (l<y-x){ 62 if (s[i-(y-x)+l+1]>s[l+1])ans=y; 63 } 64 else{ 65 l=h[y-x+1]; 66 if ((l<x-1)&&(s[y-x+l]<s[l+1]))ans=y; 67 } 68 } 69 write(ans); 70 if (i<n)putchar(' '); 71 } 72 }
3.exkmp,下面将讲述其做法——
定义数组$z_{i}=lcp(s[i,|s|],s)$(特别的,强制$z_{1}=0$)
考虑求$z_{i}$,维护一个区间$[l,r]$,其中$r=max_{1le j<i}z_{j}+j-1$,$l$为对应此$r$的最小的$j$
若$ile r$,根据$s[l,r]=s[1,r-l+1]$,有$s[i,r]=s[l-i+1,r-l+1]$,此时对$z_{i-l+1}$分类讨论:
1.$z_{i-l+1}<r-i+1$,则$z_{i}=z_{i-l+1}$(如果再增大,即$z_{i-l+1}$也能增大)
2.$z_{i-l+1}ge r-i+1$,则令$z_{i}=r-i+1$,并暴力增大$z_{i}$扩展
若$i>r$,则令$z_{i}=0$,并暴力增大$z_{i}$扩展
最后,再用$[j,z_{j}+j)$去更新$[l,r]$(当$r<z_{j}+j-1$时)
关于这一做法的正确性显然,考虑时间复杂度:对于两个暴力增大$z_{i}$扩展的情况,都是初始$i+z_{i}-1=r$,之后不断增加$z_{i}$,最终仍令$r=i+z_{i}-1$
换言之,每一次增加$z_{i}$,不妨同时令$r$增加1,那么显然总复杂度为$o(n)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 3000005 4 int n,ans,h[N],v[N],vv[N],num[11]; 5 char s[N]; 6 void write(int x){ 7 while (x){ 8 num[++num[0]]=x%10; 9 x/=10; 10 } 11 for(;num[0];num[0]--)putchar(num[num[0]]+'0'); 12 } 13 int main(){ 14 scanf("%s",s+1); 15 n=strlen(s+1); 16 int l=1,r=1; 17 for(int i=2;i<=n;i++){ 18 if (i<=r){ 19 if (h[i-l+1]<r-i+1)h[i]=h[i-l+1]; 20 else h[i]=r-i+1; 21 } 22 while ((i+h[i]<=n)&&(s[i+h[i]]==s[h[i]+1]))h[i]++; 23 if (i+h[i]-1>r){ 24 l=i; 25 r=i+h[i]-1; 26 } 27 } 28 h[1]=n; 29 for(int i=1;i<=n;i++){ 30 for(int j=0;j<=v[0];j++)vv[j]=v[j]; 31 vv[++vv[0]]=i; 32 v[0]=0; 33 for(int j=1;j<=vv[0];j++){ 34 if (!v[0])v[++v[0]]=vv[j]; 35 else{ 36 int x=vv[j],y=v[v[0]]; 37 if (s[i]<s[y+i-x])v[0]=0; 38 if ((s[i]<s[y+i-x])||(s[i]==s[y+i-x])&&(2*x-y>i))v[++v[0]]=x; 39 } 40 } 41 ans=v[1]; 42 for(int j=2;j<=v[0];j++){ 43 int x=ans,y=v[j],l=h[i-(y-x)+1]; 44 if (l<y-x){ 45 if (s[i-(y-x)+l+1]>s[l+1])ans=y; 46 } 47 else{ 48 l=h[y-x+1]; 49 if ((l<x-1)&&(s[y-x+l]<s[l+1]))ans=y; 50 } 51 } 52 write(ans); 53 if (i<n)putchar(' '); 54 } 55 }