字符串哈希 - # P3501 [POI 2010] ANT-Antisymmetry
题目描述
(https://szkopul.edu.pl/problemset/problem/EDxOyJiFZWb_PYVaYhhnhU0-/site/?key=statement)」
对于一个 \(0/1\) 字符串,如果将这个字符串 \(0\) 和 \(1\) 取反后,再将整个串反过来和原串一样,就称作「反对称」字符串。比如 \(00001111\) 和 \(010101\) 就是反对称的,而 \(1001\) 就不是。
现在给出一个长度为 \(n\) 的 \(0/1\) 字符串,求它有多少个子串是反对称的,注意这里相同的子串出现在不同的位置会被重复计算。
输入格式
第一行一个正整数 \(n\) 。
第二行一个长度为 \(n\) 的 \(0/1\) 字符串。
输出格式
一行一个整数,表示原串的反对称子串个数。
输入输出样例 #1
输入 #1
8
11001011
输出 #1
7
说明/提示
样例的 \(7\) 个反对称子串分别是:\(01\)(出现两次),\(10\)(出现两次),\(0101\),\(1100\) 和 \(001011\)。
对于 \(100\%\) 的数据, \(1\le n\le 500\ 000\) 。
题解
-
思考1: 因为“反回文”性质,回文串的长度一定是偶数。
-
思考2: 判断有多少个子串是回文串。
对于某个子串,如果前缀hash==后缀hash,该串就是回文子串。枚举区间 \([i,j]\) ,效率 \(o(n^2)\) -
思考3: 存在长度为 \(L \times 2\) 的回文串,必然存在一个长度为 \([2,(L-1) \times 2]\) 的反对称回文串。
满足二分性质, 枚举中心点,可以二分长度 L,\(nlog(n)\)
#include <iostream>
#define LL long long
#define ULL unsigned long long
using namespace std;
const int MAXN = 500010;
const ULL base1 = 131, base2 = 137;
const ULL MOD1 = 1e9 + 7, MOD2 = 1e9 + 9;
int n;
string s;
ULL pow1[MAXN], pow2[MAXN];
ULL pre1[MAXN], pre2[MAXN]; // 原串正向哈希
ULL revPre1[MAXN], revPre2[MAXN]; // 取反并反转后的哈希
void init() {
pow1[0] = pow2[0] = 1;
for (int i = 1; i <= n; i++) {
pow1[i] = pow1[i-1] * base1 % MOD1;
pow2[i] = pow2[i-1] * base2 % MOD2;
}
for (int i = 1; i <= n; i++) {
pre1[i] = (pre1[i-1] * base1 + (s[i-1] - '0')) % MOD1;
pre2[i] = (pre2[i-1] * base2 + (s[i-1] - '0')) % MOD2;
}
// 构建取反并反转后的哈希, 字符取反,方向取反。
for (int i = 1; i <= n; i++) {
int val = (s[n-i] == '0') ? 1 : 0; // 0->1, 1->0
revPre1[i] = (revPre1[i-1] * base1 + val) % MOD1;
revPre2[i] = (revPre2[i-1] * base2 + val) % MOD2;
}
}
ULL getHash(int l, int r) {
ULL h1 = (pre1[r] - pre1[l-1] * pow1[r-l+1] % MOD1 + MOD1) % MOD1;
ULL h2 = (pre2[r] - pre2[l-1] * pow2[r-l+1] % MOD2 + MOD2) % MOD2;
return h1 * MOD1 + h2;
}
ULL getRevHash(int l, int r) {
ULL h1 = (revPre1[r] - revPre1[l-1] * pow1[r-l+1] % MOD1 + MOD1) % MOD1;
ULL h2 = (revPre2[r] - revPre2[l-1] * pow2[r-l+1] % MOD2 + MOD2) % MOD2;
return h1 * MOD1 + h2;
}
bool check(int l, int r) { // 检查原串[l..r]是否反对称
int len = r - l + 1;
// 原串[l..r] 对应 取反反转串中的位置: 原l对应取反反转串的n-l+1
int revL = n - r + 1;
int revR = n - l + 1;
return getHash(l, r) == getRevHash(revL, revR);
}
int main() {
cin >> n >> s;
init();
LL ans = 0;
for (int center = 1; center < n; center++) { // 长度为偶数的回文串的中心点
int l = 0, r = min(center, n - center), best = 0;
while (l < r) {
int mid = (l + r + 1) / 2;
if (check(center - mid + 1, center + mid)) {
l = mid ;
} else {
r = mid - 1;
}
}
ans += l;
}
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号