[BZOJ2084]Antisymmetry(二分+hash)

=== ===

這裏放傳送門

=== ===

題解

首先用【看數據範圍猜複雜度大法】可以看出它的時間複雜度大概是O(nlogn) 的。。。。
然後它要統計的是符合條件的子串個數。一般統計子串的話都是枚舉一個什麼東西然後算它的貢獻?比如枚舉一個後綴,然後算這個後綴貢獻了多少個合法的子串之類的?
這個題的話因爲它有一個比較明顯的類似迴文的性質,所以可以考慮枚舉迴文中心然後計算每個點的貢獻。如何在O(logn) 或者類似的時間裏計算每個迴文中心的貢獻呢。。。
迴文串有一個比較重要的性質就是它的單調性,就是說一個迴文中心如果往兩邊延伸k個字符能構成一個迴文串,那麼往兩邊延伸1..k-1個字符也是可以構成迴文串的。
可以發現這個題的“反對稱”也是滿足這個單調性的,那麼就可以二分每一個迴文中心的貢獻了。
判定的時候使用hash,就是把正串S和取反後倒置的反串T都搞出來然後算出要求的那一段的hash值比較是否相等就可以了。

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ULL unsigned long long
using 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章