【题目链接】
https://loj.ac/problem/2452
【参考博客】
https://blog.csdn.net/xgc_woker/article/details/82904631
【题意】
在原串中找出多少组子串是反对称的,其中反对称的定义为:“该串取反 和 该串逆置是一样的”
【题解】
二分+hash,hash用两遍,记得要用二分判断最长的长度,如果最长的合法的,那么最长的半径长度就是该串为中心的方案数。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef unsigned long long ULL ; 4 const int N = 5e5+10; 5 const ULL base = 13331; 6 ULL h1[N] , h2[N] , p[N] , ans ; 7 int n,s[N] ; 8 9 /* 10 预处理 11 前缀hash h1 12 后缀hash h2 13 */ 14 void get_Hash(){ 15 p[0] = 1 ; 16 for(int i=1;i<=n;i++){ 17 p[i] = p[i-1] * base ; 18 h1[i] = h1[i-1] * base + (ULL) s[i]; 19 } 20 for(int i=n;i>=1;i--){ 21 h2[i] = h2[i+1] * base + (ULL) (s[i]^1); 22 } 23 } 24 /* 25 以i为判断的中心,其中[L,R] [x,y] 是两个半径 26 */ 27 bool check(int L,int R,int x,int y){ 28 return h1[R] - h1[L-1] * p[R-L+1] == h2[x] - h2[y+1] *p[y-x+1] ; 29 } 30 int main() 31 { 32 scanf("%d",&n); 33 for(int i=1;i<=n;i++) scanf("%1d",&s[i]); 34 get_Hash(); 35 36 for(int i=1;i<=n;i++){ 37 /* 38 注意确定上下界 39 下界必须是L=0 40 上界是到右侧的最短距离或者到左侧的最短距离 41 */ 42 int L = 0 , R = min(n-i,i) , Mid ; 43 while( L < R ){ 44 Mid = L + R + 1 >> 1 ; 45 if( check(i-Mid+1,i,i+1,i+Mid) ){ 46 L = Mid ; 47 }else { 48 R = Mid - 1 ; 49 } 50 } 51 /* 52 题目:明确说到,“将这个字符串0和1取反后,再将整个串反过来和原串一样” 53 事例中“100”并不符合,因为取反是"011",逆置是"001"。 54 这一事例充分说明奇数串是不可能的。 55 56 二分以i为最长的半径,那么比i小的都是它符合题意的反对称的串 57 */ 58 ans += L ; 59 } 60 printf("%llu\n",ans); 61 return 0 ; 62 }View Code