[ABC398F] ABCBA 题解
题意
求以字符串 \(S\) 为前缀的最短回文串。
解法
根据回文串的定义,可以将 \(S\) 翻转后(设翻转后的字符串为 \(T\))拼在其后面,但这样不一定是最优的。
显而易见的是,若 \(S\) 的后缀和 \(T\) 的相同长度的前缀相同,\(T\) 的这一前缀去除后不会影响是否满足回文条件。所以,要求最短的长度,就可以转化为求最长满足这个条件的后缀。
方法有很多,下面用的双哈希。
哈希写法
设 \(H_{s,i,p}\) 为字符串 \(s\) 前 \(i\) 个字符的第 \(p\) 个哈希,则有 \(H_{s,i,p}=B\cdot H_{s,i-1,p}+s_i\),其中 \(B\) 为模数。
设 \(\operatorname{hash}(s,l,r,p)\) 为字符串 \(s\) 从 \(l\) 到 \(r\) 的第 \(p\) 个哈希,则可以由 \(H\) 推出 \(\operatorname{hash}(s,l,r,p)=H_{s,r,p}-B^{r-l+1}\cdot H_{s,l-1,p}\)。
求出两个哈希后,直接枚举后缀长度,对 \(S\) 的后缀和 \(T\) 的前缀求哈希,若都相等则输出。
详见代码。
code
// 流星群に会えた夏
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll b1=10007,b2=10009;
int n;
string s,t;
ll tmp=0;
ll hs[514000][3],ht[514000][3],pb[514000][3];
inline ll hss(int l,int r,int i){
return hs[r][i]-hs[l-1][i]*pb[r-l+1][i];
}
inline ll hst(int l,int r,int i){
return ht[r][i]-ht[l-1][i]*pb[r-l+1][i];
}
int main(){
//ios::sync_with_stdio(false); cin.tie(0);
cin>>s; n=s.length();
for(int i=n-1;i>=0;i--) t+=s[i];
pb[0][1]=1; pb[0][2]=1;
for(int i=0;i<n;i++){
hs[i][1]=hs[i-1][1]*b1+s[i];
ht[i][1]=ht[i-1][1]*b1+t[i];
hs[i][2]=hs[i-1][2]*b2+s[i];
ht[i][2]=ht[i-1][2]*b2+t[i];
pb[i][1]=pb[i-1][1]*b1;
pb[i][2]=pb[i-1][2]*b2;
}
for(int i=n;i>=1;i--){
if(hss(n-i,n,1)==hst(0,i-1,1)&&hss(n-i,n,2)==hst(0,i-1,2)){
cout<<s;
for(int j=i;j<n;j++) cout<<t[j];
return 0;
}
}
return 0;
}

浙公网安备 33010602011771号