【hash+二分】Antisymmetry

【题目链接】

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

 

posted @ 2019-08-09 09:52  Osea  阅读(136)  评论(0编辑  收藏  举报