[loj3103]节日庆典

约定:以下字符串下标从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 } 
View Code

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 } 
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/14785609.html