字符串哈希 - # CF7D Palindrome Degree
题目描述
长度为 \(n\) 的字符串 \(s\) 被称为 \(k\)-回文串,当且仅当它本身是回文串,并且它长度为
的前缀和后缀均为 \((k-1)\)-回文串。根据定义,任何字符串(包括空串)都是 \(0\)-回文串。
我们称字符串 \(s\) 的回文度为,使得 \(s\) 为 \(k\)-回文串的最大整数 \(k\)。例如,字符串 "abaaba" 的回文度为 \(3\)。
现给定一个字符串。你的任务是计算其所有前缀的回文度之和。
输入格式
输入数据的第一行为一个非空字符串,仅包含拉丁字母和数字,字符串长度不超过 \(5 \cdot 10^{6}\)。字符串区分大小写。
输出格式
输出一个整数,表示该字符串所有前缀的回文度之和。
输入输出样例 #1
输入 #1
a2A
输出 #1
1
输入输出样例 #2
输入 #2
abacaba
输出 #2
6
题解
- 思考1: 具有dp性质, 求前缀的回文度 \(dp[i] = dp[i//2] + 1\) ,前缀i表达式为回文串时。
- 思考2: 需要实现前缀表达式是否为回文串 。
- 哈希技巧:同时维护正向哈希(左→右)和反向哈希(右→左,但权值从低到高),二者相等即回文。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5e6 + 5;
const int P1 = 31, MOD1 = 1e9 + 7;
const int P2 = 131, MOD2 = 1e9 + 9;
char s[N];
int dp[N];
ll pow1[N], pow2[N];
int main() {
scanf("%s", s + 1);
int n = strlen(s + 1);
// 预处理幂
pow1[0] = pow2[0] = 1;
for (int i = 1; i <= n; ++i) {
pow1[i] = pow1[i - 1] * P1 % MOD1;
pow2[i] = pow2[i - 1] * P2 % MOD2;
}
ll l1 = 0, l2 = 0; // 正向哈希
ll r1 = 0, r2 = 0; // 反向哈希(从右向左扩展)
ll ans = 0;
for (int i = 1; i <= n; ++i) {
// 更新正向哈希
l1 = (l1 * P1 + s[i]) % MOD1; // L[i] = L[i-1] * p + s[i]
l2 = (l2 * P2 + s[i]) % MOD2;
// 更新反向哈希(注意权值左高右低)
r1 = (r1 + s[i] * pow1[i - 1]) % MOD1; // R[i] = R[i-1] +s[i] * pow[i-1]
r2 = (r2 + s[i] * pow2[i - 1]) % MOD2;
// 双哈希同时相等才判定回文
if (l1 == r1 && l2 == r2)
dp[i] = dp[i >> 1] + 1 , ans += dp[i];
}
printf("%lld\n", ans);
return 0;
}

浙公网安备 33010602011771号