[BZOJ2084][Poi2010]Antisymmetry 二分+hash

2084: [Poi2010]Antisymmetry

Time Limit: 10 Sec  Memory Limit: 259 MB
Submit: 812  Solved: 503
[Submit][Status][Discuss]

Description

对于一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如00001111和010101就是反对称的,1001就不是。
现在给出一个长度为N的01字符串,求它有多少个子串是反对称的。

Input

第一行一个正整数N (N <= 500,000)。第二行一个长度为N的01字符串。

Output


一个正整数,表示反对称子串的个数。

Sample Input

8
11001011

Sample Output

7

hint
7个反对称子串分别是:01(出现两次), 10(出现两次), 0101, 1100和001011

HINT

 

Source

鸣谢 JZP

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cstdlib>
 5 #include<cmath>
 6 #include<algorithm>
 7 #define mod 100000007LL
 8 using namespace std;
 9 int n;
10 long long bit[500005];
11 long long b[500005],a[500005];
12 char s[500005];
13 int ans;
14 bool check(int st,int mid) {
15     if(st+mid-1>n) return 0;
16     if(st-mid<1) return 0;
17     long long x1=((b[st+mid-1]-b[st-1]*bit[mid])%mod+mod)%mod;
18     long long x2=((a[st-mid]-a[st]*bit[mid])%mod+mod)%mod;
19     return x1==x2;
20 }
21 int main() {
22     scanf("%d",&n);
23     bit[0]=1;
24     for(int i=1;i<=n;i++) {bit[i]=bit[i-1]*97%mod;}
25     scanf("%s",s+1);
26     for(int i=1;i<=n;i++) {
27         b[i]=b[i-1]*97+s[i]-'0'+1;b[i]%=mod;
28     }
29     for(int i=n;i>=1;i--) {
30         a[i]=a[i+1]*97+(((s[i]-'0')^1)+1);a[i]%=mod;
31     }
32     for(int i=1;i<=n;i++) {
33         int l=1,r=n;
34         while(l<=r) {
35             int mid=(l+r)>>1;
36             if(check(i,mid)) l=mid+1;
37             else r=mid-1;
38         }
39         ans+=(l-1);
40     }
41     printf("%d",ans);
42 }
View Code
O(∩_∩)O~ (*^__^*) 嘻嘻…… O(∩_∩)O哈哈~
原文地址:https://www.cnblogs.com/wls001/p/7723942.html