1. 程式人生 > >問題 H: 【雜湊和雜湊表】Antisymmetry

問題 H: 【雜湊和雜湊表】Antisymmetry

問題 H: 【雜湊和雜湊表】Antisymmetry

時間限制: 1 Sec  記憶體限制: 128 MB
提交: 26  解決: 5
[提交] [狀態] [討論版] [命題人:admin]

題目描述

對於一個0/1字串,如果將這個字串0和1取反後,再將整個串反過來和原串一樣,就稱作「反對稱」字串。比如00001111和010101就是反對稱的,而1001就不是。
現在給出一個長度為n的0/1字串,求它有多少個子串是反對稱的,注意這裡相同的子串出現在不同的位置會被重複計算。

輸入

第一行一個正整數n。
第二行一個長度為n的0/1字串。

輸出

一行一個整數,表示原串的反對稱子串個數。

樣例輸入

8
11001011

樣例輸出

7

提示

對於100%的資料,1≤n≤500000。

 

 

mdzz,忘記打表了,一直超時

給p^i,打個表,就過了。。。。

這題,二分+hash,一開始迴文串搞錯了,後來二分炸了,再後來一直超時。。。多災多難

 

反對稱,奇數串肯定不行,最中間那個一取反就壞了,所以一定是偶數串,然後取反一半,恰好與另一半映象,

也就是迴文串了

那麼5e5,複雜度也是n*logn之類的

那麼想到二分,列舉子串中心位置,二分子串長度。。注意二分的時候還有個小技巧,

就是拿一個變數存長度最大的可能值

再然後就是注意打一個p的i次方的表,因為反覆呼叫導致超時

 

要沒有這篇部落格我可能要死上個四五天也出不來。。

傳送門、

 

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ull,ull> pull;
const int maxn = 6e5+7;
const pull p{131LL,13331LL},one{1LL,1LL},zero{0LL,0LL};
inline pull operator - (pull a,pull b){return make_pair((a.first -  b.first),(a.second - b.second));}
inline pull operator * (pull a,pull b){return make_pair((a.first *  b.first),(a.second * b.second));}
inline pull operator + (pull a,pull b){return make_pair((a.first +  b.first),(a.second + b.second));}
inline pull operator + (pull a,int  b){return make_pair((a.first +  b      ),(a.second + b       ));}
char a[maxn];
pull Len[maxn],invLen[maxn],Pw[maxn];
bool id(char x){
    return x-'0';
}
pull Strcut(pull *Len,int l,int r){
    return Len[r] - Len[l-1]*Pw[r-l+1];
}
pull invStrcut(pull *Len,int l,int r){
    return Len[l] - Len[r+1]*Pw[r-l+1];
}
int main(){
    Pw[0] = one;
    for(int i=1;i<maxn;i++)Pw[i] = Pw[i-1]*p;
    int n;
    scanf("%d",&n);
    scanf("%s",a+1);
    Len[0] = invLen[n+1] = zero;
    for(int i=1;i<=n;i++){
        Len[i] = Len[i-1]*p + id(a[i]);
        invLen[n-i+1] = invLen[n-i+2]*p + !id(a[n-i+1]);
    }

    int l,r,mid,sul;
    ll ans = 0;

    for(int i=1;i<n;i++){
        l = 0,r = min(i,(n-i));
        while(l<=r){
            mid = (l+r)/2;
            if(Strcut(Len,i-mid+1,i) == invStrcut(invLen,i+1,i+mid))
                l = mid+1,sul = mid;
            else
                r = mid-1;
        }
        ans += sul;
    }
    printf("%lld\n",ans);

    return 0;
}

 

過了之後,瞎幾把測試,於是,單hash,並且用的無符號int

9312

84

 

上面那個雙hash 的

30408

144

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned int uint;
const int maxn = 6e5+7;
const uint p = 13331;
char a[maxn];
uint Len[maxn],invLen[maxn],Pw[maxn];
bool id(char x){
    return x-'0';
}
uint Strcut(int l,int r){
    return Len[r] - Len[l-1]*Pw[r-l+1];
}
uint invStrcut(int l,int r){
    return invLen[l] - invLen[r+1]*Pw[r-l+1];
}
int main(){
    Pw[0] = 1;
    for(int i=1;i<maxn;i++)
        Pw[i] = Pw[i-1]*p;
    int n;
    scanf("%d",&n);
    scanf("%s",a+1);
    Len[0] = invLen[n+1] = 0;
    for(int i=1;i<=n;i++){
        Len[i] = Len[i-1]*p + id(a[i]);
        invLen[n-i+1] = invLen[n-i+2]*p + !id(a[n-i+1]);
    }
    int l,r,mid,sul;
    ll ans = 0;
    for(int i=1;i<n;i++){
        if(a[i]==a[i+1])continue;
        l = 1,r = min(i,(n-i));
        while(l<=r){
            mid = (l+r)/2;
            if(Strcut(i-mid+1,i) == invStrcut(i+1,i+mid))
                l = mid+1,sul = mid;
            else
                r = mid-1;
        }
        ans += sul;
    }
    printf("%lld\n",ans);
    return 0;
}