BZOJ2084[Poi2010] Antisymmetry

30 篇文章 0 订阅
23 篇文章 1 订阅

BZOJ2084[Poi2010] Antisymmetry

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

Solution:

挺不错的一道题..(其实是因为你自己YY出来了吧

最开始想枚举一点,计算另一点,然后就怎么也弄不出来。后来可以发现一些性质:

  • 反对称子串长度一定是偶数(这不废话吗
  • 反对称子串的中心串一定是反对称子串:设原串的区间为 [L,R] ,则 [L+1,R1],[L+2,R2],[L+3,R3] 等等都是反对称子串。

一旦发现了这第二个性质,就非常简单了。枚举中心点,二分答案最长的以此为中心的反对称子串,哈希一下就好了。复杂度 O(nlogn)

话说这题好像用Manacher算法可以做到 O(n) ,以后再说吧。

#include<stdio.h>
#include<iostream>
#define P 1000000007
#define B 200019
#define M 500005
#define ll long long
using namespace std;
int Hash1[M],Hash2[M],Base[M],n;
char str[M],str1[M];
bool check(int i,int j){
    int t1=((Hash1[j]-1LL*Hash1[i]*Base[j-i])%P+P)%P;
    int t2=((Hash2[n-i+1]-1LL*Hash2[n-j+1]*Base[j-i])%P+P)%P;
    return t1==t2;
}
void swap(char &a,char &b){char t=a;a=b;b=t;}
int min(int a,int b){return a<b?a:b;}
int main(){
    ll ans=0;
    scanf("%d",&n);
    scanf("%s",str+1);
    Base[0]=1;
    for(int i=1;i<=n;i++)
        Base[i]=1LL*Base[i-1]*B%P;
    for(int i=1;i<=n;i++)
        Hash1[i]=(1LL*Hash1[i-1]*B+str[i])%P;
    for(int i=1;i<=n;i++)
        str1[i]=(str[i]=='0'?'1':'0');
    int i1=1,j1=n;
    while(i1<=j1)swap(str1[i1++],str1[j1--]);
    for(int i=1;i<=n;i++)
        Hash2[i]=(1LL*Hash2[i-1]*B+str1[i])%P;
    for(int i=1;i<n;i++){
        int j=i+1;
        if(str[i]==str[i+1])continue;
        int L=0,R=min(i-1,n-i-1),res=L;
        while(L<=R){
            int mid=(L+R)>>1;
            if(check(i-mid,i+1+mid)){
                L=mid+1;
                res=mid;
            }else R=mid-1;
        }
        ans+=res+1;
    }
    cout<<ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值