LOJ 2452 对称 Antisymmetry——用hash求回文串数
概念
用hash求最长回文串/回文串数
首先,易知,回文串具有单调性。
如果字符串 $s[l...r]$ 为回文串串,那么 $s[x...y]$($l < x, y < r$ 且 $|l-x| = |r-y|$)也一定是回文串。
因此,可以二分。
通常,枚举一下起点或者中点,然后二分长度。
这样复杂度为 $O(nlogn)$,逊色于马拉车 $O(n)$,但在时限不那么紧的情况下,hash也是不错的选择。
例题
题意:对于一个 0/1 串,如果将这个字符串 0 和 1 取反后,再将整个串反过来和原串一样,就称作「反对称」字符串。先给出一个长为 $n$ 的 0/1 串,求它有多少反对称字串。($1 \leq n\leq 500000$)
分析:
先正向求一遍hash,再反向求一遍hash(0,1互换)。
枚举中点,判断左右两半的hash值是否相同。
显然,不存在长度为奇数的反对称串。
#include <bits/stdc++.h> using namespace std; typedef unsigned long long ull; typedef long long ll; const ull base = 233; const int maxn = 5e5 + 10; ull h1[maxn], h2[maxn], p[maxn]; int n; ll ans; char s[maxn]; ull get_h1(int l, int r) { return h1[r] - h1[l-1]*p[r-l+1]; } ull get_h2(int l, int r) { return h2[l] - h2[r+1]*p[r-l+1]; } int check(int x) { int l=1, r = min(x, n-x); int ans=0; while(l <= r) { int mid = (l+r) >> 1; if(get_h1(x-mid+1,x) == get_h2(x+1, x+mid)) { ans = mid; l = mid + 1; } else r = mid - 1; } return ans; } int main() { scanf("%d%s", &n, s+1); p[0] = 1; for(int i = 1;i <= n;i++) { p[i] = p[i-1]*base; h1[i] = h1[i-1]*base + (ull)s[i]; } for(int i = n;i >= 1;i--) h2[i] = h2[i+1]*base + (ull)(s[i] == '0' ? s[i]+1 : s[i]-1); for(int i = 1;i < n;i++) ans += check(i); printf("%lld\n", ans); return 0; }
参考链接:https://www.cnblogs.com/henry-1202/p/10321013.html
个性签名:时间会解决一切