ANT-Antisymmetry
題目
對於一個01字符串,如果將這個字符串0和1取反後,再將整個串反過來和原串一樣,就稱作“反對稱”字符串。比如00001111和010101就是反對稱的,1001就不是。
現在給出一個長度爲N的01字符串,求它有多少個子串是反對稱的。
輸入
第一行n
第二行n個01字符串
輸出
它有多少個子串是反對稱的
輸入樣例
8
11001011
輸出樣例
7
解題思路
其實這道題就是一道哈希加二分,推出來可以得出反過來的01串加上原本的可以發現是一個迴文,那麼就很好解決了。枚舉中心,不斷的看k格,然後就哈希看是否爲迴文即可.
程序如下
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define ll long long
const ll math=2000001001;
using namespace std;
int n,j,l,r,mid,maxn;
ll hx[500001],re[500001],hxx[500001],ans;
char a[500001];
bool check(int l,int r,int m)
{
ll s1,s2;
int h1,h2;
h1 = l + m - 1;
h2 = r + m - 1;
s1 = hx[h1] - hx[l - 1];//看是否爲迴文
s2 = hxx[h2] - hxx[r - 1];
if(l > r)
{
swap(s1,s2);
swap(l,r);
}
s1 *= re[r - l];
return s1 == s2;
}
int main()
{
scanf("%d",&n);
scanf("%s",&a);
re[0]=1;
for(int i = 1;i <= n;++ i)
re[i] = re[i - 1] * math;
for(int i = 1;i <= n;++ i)
hx[i] = hx[i - 1] + a[i - 1] * re[i];//把字符串轉化成數值
for(int i = 1;i <= n / 2;++ i)
swap(a[i - 1],a[n - i]);//交換兩邊的
for(int i = 1;i <= n;++ i)
if(a[i - 1] == '0') a[i - 1] = '1';//改變
else a[i - 1] = '0';
for(int i = 1;i <= n;++ i)
hxx[i] = hxx[i - 1] + a[i - 1] * re[i];
for(int i = 2;i <= n;++ i)
{
j = n - i + 2;
l = 1;
r = min(n - i + 1,n - j + 1);
maxn=0;
while(l <= r)//二分
{
mid = (l+r) >> 1;
if(check(i,j,mid))
{
maxn = max(maxn,mid);
l = mid + 1;
}
else r = mid - 1;
}
ans += maxn;
}
printf("%lld",ans);
return 0;
}