约定:以下字符串下标从1开始
定义$s$为偶回文串,当且仅当$s$的长度为偶数且$s$为回文串
假设$s=ABC$,考虑$t$的情况,即$ABC$的全排列——
1.$t=ABC$,即$s=t$,由于$nge 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 e t$,否则显然之前已经得到答案)
记$l=|s|=|t|$,$l_{A}=max_{1le 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 1le ile |y_{1}|,P_{i}=P_{|y_{1}|-i+1}=P_{i+|v|}$
下面考虑结论(不妨仅考虑$x_{1}$),即求证$forall 1le ile |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_{0le jle min(i,2l-i),s(i-j,i+j]为回文串}j$,即以$i$和$i+1$为中心的最长偶回文串(长度的一半),显然这个长度具有单调性(即满足条件的$j$是从0开始的连续区间)
考虑求$d_{i}$,维护区间$[l,r]$,其中$r=max_{1le 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}$
(关于$ile 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 1 1 2 2 3 %d ",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 %d %d 1 1 2 %d ",i+1,n,i); 42 else printf("YES %d %d %d %d 1 %d ",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 1 %d %d %d %d %d ",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 1 %d %d %d %d %d ",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 %d %d 1 %d %d %d ",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 %d %d 1 %d %d %d ",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 %d %d %d %d 1 %d ",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 %d %d %d %d 1 %d ",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 "); 179 } 180 return 0; 181 }