问题 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;
}

猜你喜欢

转载自blog.csdn.net/Du_Mingm/article/details/83590376