[luogu3785]文本校正

约定:以下字符串下标从1开始

定义$s$为偶回文串,当且仅当$s$的长度为偶数且$s$为回文串

假设$s=ABC$,考虑$t$的情况,即$ABC$的全排列——

1.$t=ABC$,即$s=t$,由于$n\ge 3$,随便划分即可

2.$t=BCA$或$t=CAB$,即将$s$旋转后变为$t$,枚举$|A|$和$|AB|$(或$|BC|$和$|C|$)并哈希判定即可

(注意最后划分要求非空,需要对旋转的位置分类讨论)

3.$t=ACB$或$t=BAC$或$t=CBA$,枚举其中某一段并直接比较或哈希(第3类)来判定是否相等,剩下可以看作一个划分为两段的子问题

更具体的,即判定是否存在$AB$使得$s=AB$且$t=BA$

(为了方便,这里认为$s\ne t$,否则显然之前已经得到答案)

记$l=|s|=|t|$,$l_{A}=\max_{1\le i<l,s[1,i]=t(l-i,l]}i$,$l_{B}$类似(即$|B|$的最大值)

结论:若存在$AB$,则存在$AB$满足$|A|=l_{A}$或$|B|=l_{B}$

构造字符串$P=s_{1}t_{l}s_{2}t_{l-1}...s_{n}t_{1}$,实际上就是将$P$划分为两个偶回文串

上面所述的$l_{A}$和$l_{B}$,也即$P$最长为偶回文串的前缀和后缀(长度的一半)

考虑一个命题,即:假设$P=x_{1}x_{2}=y_{1}y_{2}=z_{1}z_{2}$,并满足$|x_{1}|<|y_{1}|<|z_{1}|$且$x_{2},y_{1},y_{2},z_{1}$都是偶回文串,求证$x_{1}$和$z_{2}$是偶回文串

若能证明上述命题,取$x_{1}x_{2}$为$|B|=l_{B}$的划分,$z_{1}z_{2}$为$|A|=l_{A}$的划分,$y_{1}y_{2}$为任意合法划分,$|y_{1}|=|x_{1}|$或$|z_{1}|$即直接成立,$|x_{1}|>|z_{1}|$即$l_{A}+l_{B}<l$显然无解,否则即可证明$x_{1}x_{2}$和$y_{1}y_{2}$都合法

下面,来证明此命题——

令$v=P(|y_{1}|,|z_{1}|]$,根据$y_{2}$回文和$x_{2}$回文,可得$v=P(2l-|v|,2l]=P(|x_{1}|,|x_{1}|+|v|]$

另一方面,根据$y_{1}$和$z_{1}$回文,可得$\forall 1\le i\le |y_{1}|,P_{i}=P_{|y_{1}|-i+1}=P_{i+|v|}$

下面考虑结论(不妨仅考虑$x_{1}$),即求证$\forall 1\le i\le |x_{1}|,P_{i}=P_{|x_{1}|-i+1}$,将两者都在$v$中表示:

对于前者,令$i_{1}=i$,并将其不断加上$|v|$直至$i_{1}\in (|x_{1}|,|x_{1}|+|v|]$,最终即等于$v_{i_{1}-|x_{1}|}$

对于后者,根据$z_{1}$回文,可得$P_{|x_{1}|-i+1}=P_{|z_{1}|-|x_{1}|+i}$,令$i_{2}=|z_{1}|-|x_{1}|+i$,同样将其不断加上$|v|$直至$i_{2}\in (y_{1},z_{1}]$,最终即等于$v_{i_{2}-|y_{1}|}$

问题即求证$i_{1}-|x_{1}|=i_{2}-|y_{1}|$,也即$i-|x_{1}|\equiv |z_{1}|-|x_{1}|+i-|y_{1}|(mod\ |v|)$,显然成立

综上所述,只需要判定$|A|=l_{A}$和$|B|=l_{B}$的情况,具体判定可以用哈希,问题即变为求$l_{A}$和$l_{B}$

由于问题需要更细化,再对$ABC$的排列情况分类讨论——

(1)$t=ACB$或$t=BAC$(以下仅考虑前者),此时是枚举$|A|$,之后剩下的是$s$和$t$的一个后缀

再以求$l_{A}$为例,即求$s$从$|A|+1$开始,最长的串使得其是$t$的后缀,那么将$t$接到$s$后面,也就是一个后缀的(最长)border长度,翻转后即求前缀的border长度,KMP即可

(2)$t=CBA$,此时是枚举$|A|$并哈希判定,之后剩下的是$s$的一个后缀和$t$的一个前缀

这个问题中,$l_{A}$和$l_{B}$就不同了,下面分类讨论:

1.对于$l_{B}$,预处理出来所有可行的,并找到第一个小于等于此长度的(利用单调性可以做到线性)

2.对于$l_{A}$,考虑前面证明时的构造,即令$P=s_{1}t_{n}s_{2}t_{n-1}...s_{n}t_{1}$(这里的$s$和$t$指最初的$s$和$t$),对于$l_{A}$即求从$i$开始的最长偶回文串(长度),使用manacher即可

下面将讲述manacher做法——

定义$d_{i}=\max_{0\le j\le \min(i,2l-i),s(i-j,i+j]为回文串}j$,即以$i$和$i+1$为中心的最长偶回文串(长度的一半),显然这个长度具有单调性(即满足条件的$j$是从0开始的连续区间)

考虑求$d_{i}$,维护区间$[l,r]$,其中$r=\max_{1\le j<i}j+d_{j}$,$l$为对应此$r$的最小的$j$时的$j-d_{j}+1$

若$i<r$,考虑$j=l+r-i-1$,根据$s[l,r]$为回文串,有$s[l,2j-l+1]$与$s(2i-r,r]$两者互为翻转的结果,其中前者以$j$和$j+1$为中心,后者以$i$和$i+1$为中心

注意到$s$是回文串则将$s$翻转后也为回文串,因此$d_{i}\ge \max(d_{j},j-l+1)$,此时对$d_{j}$分类讨论:

1.$d_{j}<j-l+1$,则$d_{i}=d_{j}$(如果再增大,即$d_{j}$也能增大)

2.$d_{i}\ge j-l+1$,则令$d_{i}=j-l+1$,并暴力增大$d_{i}$扩展

若$i>r$,则令$d_{i}=0$,并暴力增大$d_{i}$扩展

最后,再用$(j-d_{j},j+d_{j}]$去更新$[l,r]$(当$r<j+d_{j}$时)

(特别的,初始状态设置为$l=1$和$r=0$,从$i=1$开始)

关于这一做法的正确性显然,考虑时间复杂度:对于两个暴力增大$d_{i}$扩展的情况,都是初始$i+d_{i}=r$,之后不断增加$d_{i}$,最终仍令$r=i+d_{i}$

(关于$i\le r$的第2种情况,代入$j$的式子即可)

换言之,每一次增加$d_{i}$,不妨同时令$r$增加1,那么显然总复杂度为$o(n)$

(上面的manacher仅仅是对偶数部分,当然奇数部分也是类似的,但本题中只需要偶数即可)

接下来,求出以$i$为起点的最长偶回文串,即找到最大的$j$满足$j-d_{j}<i$,答案即$j-i+1$,显然用单调队列维护即可,可以做到线性

最终,总复杂度为$o(n)$,可以通过

(另外此题在loj上似乎有一些问题,可以在洛谷上提交)

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define N 1000005
  4 #define base 1000007
  5 #define mod 998244353
  6 int t,n,m,a[N],b[N],mi[N],suma[N],sumb[N],c[N<<1],nex[N<<1],d[N<<1];
  7 char ans[N][3];
  8 int read(){
  9     int x=0;
 10     char c=getchar();
 11     while ((c<'0')||(c>'9'))c=getchar();
 12     while ((c>='0')&&(c<='9')){
 13         x=x*10+c-'0';
 14         c=getchar();
 15     }
 16     return x;
 17 }
 18 int hasha(int l,int r){
 19     return (suma[r]-1LL*mi[r-l+1]*suma[l-1]%mod+mod)%mod;
 20 }
 21 int hashb(int l,int r){
 22     return (sumb[r]-1LL*mi[r-l+1]*sumb[l-1]%mod+mod)%mod;
 23 }
 24 int main(){
 25     mi[0]=1;
 26     for(int i=1;i<N;i++)mi[i]=1LL*mi[i-1]*base%mod;
 27     t=read();
 28     while (t--){
 29         n=read(),read();
 30         for(int i=1;i<=n;i++)a[i]=read();
 31         for(int i=1;i<=n;i++)b[i]=read();
 32         for(int i=1;i<=n;i++)suma[i]=(1LL*suma[i-1]*base+a[i])%mod;
 33         for(int i=1;i<=n;i++)sumb[i]=(1LL*sumb[i-1]*base+b[i])%mod;
 34         if (hasha(1,n)==hashb(1,n)){
 35             printf("YES\n1 1\n2 2\n3 %d\n",n);
 36             continue;
 37         }
 38         bool flag=0;
 39         for(int i=1;i<n;i++)
 40             if ((hasha(1,n-i)==hashb(i+1,n))&&(hasha(n-i+1,n)==hashb(1,i))){
 41                 if (i>1)printf("YES\n%d %d\n1 1\n2 %d\n",i+1,n,i);
 42                 else printf("YES\n%d %d\n%d %d\n1 %d\n",i+1,i+1,i+2,n,i);
 43                 flag=1;
 44                 break;
 45             }
 46         if (flag)continue;
 47         for(int i=0;i<n;i++){
 48             c[i]=a[i+1];
 49             c[i+n]=b[i+1];
 50         }
 51         for(int i=0;i<n;i++)swap(c[i],c[2*n-i-1]);
 52         nex[0]=nex[1]=0;
 53         for(int i=1,j=0;i<2*n;i++){
 54             while ((j)&&(c[j]!=c[i]))j=nex[j];
 55             if (c[j]==c[i])j++;
 56             nex[i+1]=j;
 57         }
 58         for(int i=1;(i<=n)&&(a[i]==b[i]);i++){
 59             int l=nex[2*n-i];
 60             if ((l)&&(hasha(i+l+1,n)==hashb(i+1,n-l))){
 61                 printf("YES\n1 %d\n%d %d\n%d %d\n",i,n-l+1,n,i+1,n-l);
 62                 flag=1;
 63                 break;
 64             }
 65         }
 66         if (flag)continue;
 67         for(int i=0;i<n;i++){
 68             c[i]=b[i+1];
 69             c[i+n]=a[i+1];
 70         }
 71         for(int i=0;i<n;i++)swap(c[i],c[2*n-i-1]);
 72         nex[0]=nex[1]=0;
 73         for(int i=1,j=0;i<2*n;i++){
 74             while ((j)&&(c[j]!=c[i]))j=nex[j];
 75             if (c[j]==c[i])j++;
 76             nex[i+1]=j;
 77         }
 78         for(int i=1;(i<=n)&&(a[i]==b[i]);i++){
 79             int l=nex[2*n-i];
 80             if ((l)&&(hasha(i+1,n-l)==hashb(i+l+1,n))){
 81                 printf("YES\n1 %d\n%d %d\n%d %d\n",i,i+l+1,n,i+1,i+l);
 82                 flag=1;
 83                 break;
 84             }
 85         }
 86         if (flag)continue;
 87         for(int i=0;i<n;i++){
 88             c[i]=a[i+1];
 89             c[i+n]=b[i+1];
 90         }
 91         nex[0]=nex[1]=0;
 92         for(int i=1,j=0;i<2*n;i++){
 93             while ((j)&&(c[j]!=c[i]))j=nex[j];
 94             if (c[j]==c[i])j++;
 95             nex[i+1]=j;
 96         }
 97         for(int i=n;(i)&&(a[i]==b[i]);i--){
 98             int l=nex[n+i-1];
 99             if ((l)&&(hasha(l+1,i-1)==hashb(1,i-l-1))){
100                 printf("YES\n%d %d\n1 %d\n%d %d\n",i-l,i-1,i-l-1,i,n);
101                 flag=1;
102                 break;
103             }
104         }
105         if (flag)continue;
106         for(int i=0;i<n;i++){
107             c[i]=b[i+1];
108             c[i+n]=a[i+1];
109         }
110         nex[0]=nex[1]=0;
111         for(int i=1,j=0;i<2*n;i++){
112             while ((j)&&(c[j]!=c[i]))j=nex[j];
113             if (c[j]==c[i])j++;
114             nex[i+1]=j;
115         }
116         for(int i=n;(i)&&(a[i]==b[i]);i--){
117             int l=nex[n+i-1];
118             if ((l)&&(hasha(1,i-l-1)==hashb(l+1,i-1))){
119                 printf("YES\n%d %d\n1 %d\n%d %d\n",l+1,i-1,l,i,n);
120                 flag=1;
121                 break;
122             }
123         }
124         if (flag)continue;
125         c[0]=0;
126         for(int i=n;i;i--)
127             if (hasha(i,n)==hashb(1,n-i+1))c[++c[0]]=i;
128         for(int i=1;i<=n;i++)
129             if (hasha(1,i)==hashb(n-i+1,n)){
130                 while ((c[0])&&(c[c[0]]<=i))c[0]--;
131                 int l=c[c[0]];
132                 if ((l)&&(hasha(i+1,l-1)==hashb(n-l+2,n-i))){
133                     printf("YES\n%d %d\n%d %d\n1 %d\n",n-i+1,n,n-l+2,n-i,n-l+1);
134                     flag=1;
135                     break;
136                 }
137             }
138         if (flag)continue;
139         for(int i=1;i<=n;i++){
140             c[2*i-1]=a[i];
141             c[2*i]=b[n-i+1];
142         }
143         int l=1,r=0;
144         for(int i=1;i<=2*n;i++){
145             if (i<r){
146                 int j=l+r-i-1;
147                 if (d[j]<j-l+1)d[i]=d[j];
148                 else{
149                     d[i]=j-l+1;
150                     while ((i-d[i]>0)&&(i+d[i]<2*n)&&(c[i-d[i]]==c[i+d[i]+1]))d[i]++;
151                 }
152             }
153             else{
154                 d[i]=0;
155                 while ((i-d[i]>0)&&(i+d[i]<2*n)&&(c[i-d[i]]==c[i+d[i]+1]))d[i]++;
156             }
157             if (i+d[i]>r){
158                 l=i-d[i]+1;
159                 r=i+d[i];
160             }
161         }
162         int x=1,y=0;
163         for(int i=2*n;i;i--){
164             while ((x<=y)&&(c[x]-d[c[x]]>=i))x++;
165             if (x>y)nex[i]=0;
166             else nex[i]=c[x]-i+1;
167             if ((x>y)||(c[y]-d[c[y]]>i-d[i]))c[++y]=i;
168         }
169         for(int i=1;i<=n;i++)
170             if (hasha(1,i)==hashb(n-i+1,n)){
171                 int l=nex[2*i+1];
172                 if ((l)&&(hasha(i+l+1,n)==hashb(1,n-i-l))){
173                     printf("YES\n%d %d\n%d %d\n1 %d\n",n-i+1,n,n-i-l+1,n-i,n-i-l);
174                     flag=1;
175                     break;
176                 }
177             }
178         if (!flag)printf("NO\n");
179     }
180     return 0;
181 }
View Code

 

posted @ 2021-05-20 16:18  PYWBKTDA  阅读(148)  评论(0编辑  收藏  举报