思路
我们先观察 \(\texttt{aauuhhhua}\) 这个字符串,如果把后三个字符 \(\texttt{hua}\) 各自就地重复一次(在后面插入一个相同字符),那么整个字符串就是 \(\texttt{aauuhhhhuuaa}\),变成一个回文串了。
更一般地,一个长度为 \(3k\) 的串 \(T\) 满足题目要求,当且仅当把 \(T\) 的后 \(k\) 个字符各自重复一次之后,得到的长度为 \(4k\) 的串是回文串。
判断回文串可以用字符串哈希实现,但是怎么实现每个字符在它的后面重复一次的操作呢?换言之,某个前缀哈希值代表的是前缀串所有字符重复两次的哈希值,怎么实现?
举个例子:
以下固定一个基数 \(p\),\(s_i\) 表示 \(s\) 的第 i 个元素的权值。
原串 \(s\) 的前缀哈希定义为:
展开得:
现在,我定义一个二倍哈希数组 \(dh\)。我要一步当两步走,\(dh_1 = h_2\),\(dh_2 = h_4\),将 \(h_1\) 和 \(h_3\) 的值代入 \(h_2\) 和 \(h_4\) 的递推式中得到:
于是我们就得到了 \(dh\) 的递推式的一般形式:
还有最后一个问题,那就是应该怎么计算区间 \([l,r]\) 的二倍哈希值?
推导如下:
我们需要用上面的方法,取出子串 \(T\) 的后 \(k\) 个字符,将它们加倍后反转,再与前 \(2k\) 个字符比较。
加倍哈希的区间查询公式对任意区间都成立。如果我们预处理的是从右往左构建的加倍哈希,就可以直接截取后缀区间的加倍值,而不用做额外的反转操作。
具体地,设 \(dh_i\) 为 \(s[i \dots n]\)(即后缀)反转后每个字符加倍的前缀哈希,从右往左递推:
此时,区间 \([c,r]\) 的反向加倍哈希就是 \(dh_c - dh_{r+1} \times p^{2(r-c+1)}\)。
至此思路完结。
代码细节
- 二倍哈希是倒序更新的;
- 注意二分的左右边界以及判断区间的左右端点;
- 常用的哈希底数和模数 \((31,10^9+7)\) 能过;
复杂度分析
二分搭配哈希求回文串的变式,总体时间复杂度 \(O(n \log n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"
#define div() cout << "-----------------------\n";
#define debug(n) cout << #n << " = " << n << "\n";
#define deb(n) cout << #n << " = " << n << " ";
int n;string s;int a[1000005];int ans;
int h[1000005];int fh[1000005];int base[1000005];
const int p = 31,mod = 1e9+7;
inline void work(int center){
int l = 0,r = min(center/2,n-center+1),mid = 0;int len = 0;
while(l <= r){
mid = (l+r) >> 1;
int lb = center-mid-mid;
int rb = center+mid-1;
int h1 = (h[center-1]-h[lb-1]*base[center-lb]%mod+mod)%mod;
int h2 = (fh[center]-fh[rb+1]*base[(rb-center+1)*2]%mod+mod)%mod;
if(h1 == h2){
len = mid;
l = mid+1;
}
else{r = mid-1;}
}
ans += len;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin >> s;n = s.size();
for(int i = 1;i <= n;i++){a[i] = s[i-1]-'a'+1;}
base[0] = 1;
for(int i = 1;i <= 2*n;i++){base[i] = base[i-1]*p%mod;}
for(int i = 1;i <= n;i++){h[i] = (h[i-1]*p+a[i])%mod;}
for(int i = n;i >= 1;i--){fh[i] = (fh[i+1]*p*p+(p+1)*a[i])%mod;}
for(int i = 1;i <= n;i++){work(i);}
cout << ans;
return 0;
}
浙公网安备 33010602011771号