Codeforces961F. k-substrings

$n \leq 1000000$的字符串,对每一个子串$i$~$n-i+1$,求他最长的一个既是前缀又是后缀的子串。

这题要求的东西具有“对称性”,不充分利用难以解决。这里的“对称性”不仅指询问是对称的,更指要求的那个公共部分是对称的——不对称的相同的子串对答案没有丝毫贡献。

从贡献的角度入手,就是求每个前缀$i$和后缀$n-i+1$的一个在前缀$i$的末尾的、在后缀$n-i+1$的开头的一个最长公共串。嗯那赶紧二分+哈希。额等等,从$i$向前延伸,从$n-i+1$向后延伸,字符串是否相同,这样一个函数不单调哦。但是,如果您从前缀$i$的中点处和后缀$n-i+1$的中点处向两边分别延伸,这样字符串是否相同就是一个单调函数了。

因此转移战线,枚举对称相同串的中间位置$i$,然后$i$和$n-i+1$分别向两边延伸看最长的相同串多长,用二分+哈希判断。假设这次二分的答案是$2x-1$,那么$ans_{i-x+1}$就会$max$上$2x-1$。但这个串对$i-x+1$到$i$处的答案都是有贡献的,并且贡献每次减2,为了体现这一点只需要最后统计答案时$ans_i \ \ =max(ans_i,ans_{i-1}-2)$即可。

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 //#include<queue>
 5 //#include<vector>
 6 #include<algorithm>
 7 //#include<iostream>
 8 //#include<assert.h>
 9 using namespace std;
10 
11 int n;
12 #define maxn 2000011
13 char s[maxn];
14 
15 int h1[maxn],h2[maxn],p1[maxn],p2[maxn],mod1=998244353,mod2=1000000007;
16 int geth1(int L,int R) {return (h1[R]-h1[L-1]*1ll*p1[R-L+1]%mod1+mod1)%mod1;}
17 int geth2(int L,int R) {return (h2[R]-h2[L-1]*1ll*p2[R-L+1]%mod2+mod2)%mod2;}
18 
19 int ans[maxn];
20 int main()
21 {
22     scanf("%d%s",&n,s+1);
23     h1[0]=h2[0]=0;
24     for (int i=1;i<=n;i++) h1[i]=(h1[i-1]*27ll+s[i]-'a'+1)%mod1,h2[i]=(h2[i-1]*27ll+s[i]-'a'+1)%mod2;
25     p1[0]=p2[0]=1;
26     for (int i=1;i<=n;i++) p1[i]=p1[i-1]*27ll%mod1,p2[i]=p2[i-1]*27ll%mod2;
27     
28     for (int i=0;i<=(n+1)>>1;i++) ans[i]=-1;
29     for (int i=1;i<=n>>1;i++)
30     {
31         int L=0,R=i,p=n-i+1;
32         while (L<R)
33         {
34             int mid=(L+R+1)>>1,a=i-mid+1,b=i+mid-1,c=p-mid+1,d=p+mid-1;
35             if (geth1(a,b)==geth1(c,d) && geth2(a,b)==geth2(c,d)) L=mid;
36             else R=mid-1;
37         }
38         ans[i-L+1]=max(ans[i-L+1],2*L-1);
39     }
40     for (int i=1;i<=(n+1)>>1;i++)
41     {
42         ans[i]=max(ans[i],ans[i-1]-2);
43         printf("%d ",ans[i]);
44     }
45     return 0;
46 }
View Code

 

posted @ 2018-04-06 16:52  Blue233333  阅读(202)  评论(0编辑  收藏  举报