[BZOJ2084]Antisymmetry(二分+hash)

来源:互联网 发布:淘宝优惠助手 编辑:程序博客网 时间:2024/06/06 08:43

=== ===

这里放传送门

=== ===

题解

首先用【看数据范围猜复杂度大法】可以看出它的时间复杂度大概是O(nlogn)的。。。。
然后它要统计的是符合条件的子串个数。一般统计子串的话都是枚举一个什么东西然后算它的贡献?比如枚举一个后缀,然后算这个后缀贡献了多少个合法的子串之类的?
这个题的话因为它有一个比较明显的类似回文的性质,所以可以考虑枚举回文中心然后计算每个点的贡献。如何在O(logn)或者类似的时间里计算每个回文中心的贡献呢。。。
回文串有一个比较重要的性质就是它的单调性,就是说一个回文中心如果往两边延伸k个字符能构成一个回文串,那么往两边延伸1..k-1个字符也是可以构成回文串的。
可以发现这个题的“反对称”也是满足这个单调性的,那么就可以二分每一个回文中心的贡献了。
判定的时候使用hash,就是把正串S和取反后倒置的反串T都搞出来然后算出要求的那一段的hash值比较是否相等就可以了。

代码

#include<cstdio>#include<cstring>#include<algorithm>#define ULL unsigned long longusing namespace std;const ULL wer=2000001001;int n;long long ans;char st[500010];ULL hash[500010],rhash[500010],mul[500010];bool same(int s1,int s2,int len){    int t1,t2,dlt;    ULL h1,h2;    t1=s1+len-1;t2=s2+len-1;    h1=hash[t1]-hash[s1-1];    h2=rhash[t2]-rhash[s2-1];    if (s1>s2){swap(s1,s2);swap(h1,h2);}    h1*=mul[s2-s1];    return h1==h2;}long long divide(int s1,int s2,int l,int r){    int mid,ans=0;    while (l<=r){        mid=(l+r)>>1;        if (same(s1,s2,mid)){            ans=max(ans,mid);l=mid+1;        }else r=mid-1;    }    return ans;}int main(){    scanf("%d\n",&n);    gets(st);    mul[0]=1;    for (int i=1;i<=n;i++) mul[i]=mul[i-1]*wer;    for (int i=1;i<=n;i++)      hash[i]=hash[i-1]+st[i-1]*mul[i];    for (int i=1;i<=n/2;i++) swap(st[i-1],st[n-i]);    for (int i=1;i<=n;i++)      if (st[i-1]=='0') st[i-1]='1';      else st[i-1]='0';    for (int i=1;i<=n;i++)      rhash[i]=rhash[i-1]+st[i-1]*mul[i];    for (int i=2;i<=n;i++){        int j=n-i+1+1,len;        len=min(n-i+1,n-j+1);        ans+=divide(i,j,1,len);    }    printf("%I64d\n",ans);    return 0;}
0 0
原创粉丝点击