反对称 Antisymmetry (manacher)(hash+二分)

对于一个 0/1 字符串,如果将这个字符串 0 和 1 取反后,再将整个串反过来和原串一样,就称作「反对称」字符串。比如 00001111 和 010101 就是反对称的,而 1001 就不是。
现在给出一个长度为 n的 0/1字符串,求它有多少个子串是反对称的,注意这里相同的子串出现在不同的位置会被重复计算。


这道题有两种做法

1:用hash加快判断,二分长度

2:用manacher的变形,改变判断条件

hash就不讲了

直接上代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#define base 10000000007
#define min(a,b) (a) > (b) ? (b) : (a)
using namespace std;
inline long long rd(){
    long long x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
inline void write(long long x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
    return ;
}
long long n;
char a[1000006],s[1000006];
long long x[1000006],y[1000006];
long long po[1000006];
inline bool check(long long h,long long v){
    if(v-h-1<0&&v==(n/2)||v+h+2>n+1&&v+1==(n/2)+1) return 0;
    long long sum1=x[v]-x[v-h-1]*po[h+1];
    long long sum2=y[v+1]-y[v+h+2]*po[h+1];
    //cout<<h<<" "<<sum1<<" "<<sum2<<endl; 
    return (sum1==sum2);
}
int main(){
    po[0]=1;
    for(long long i=1;i<=500006;i++) po[i]=po[i-1]*base;
    n=rd();
    scanf("%s",a+1);
    for(long long i=1;i<=n;i++){
        s[i]=a[i]-'0';
        x[i]=x[i-1]*base+s[i]+1;
    }
    for(long long i=n;i>=1;i--) y[i]=y[i+1]*base+(s[i]^1)+1;
    long long ans=0;
    for(long long i=1;i<n;i++){
        if(s[i]!=s[i+1]) ans++;
        else continue;
        long long set=0;
        long long l=0,r=min(i,n-i);
        while(l<=r){
            long long mid=(l+r)/2;
            if(check(mid,i)){
                l=mid+1;
                set=mid;
            }
            else r=mid-1;
        }
        ans+=set;
    }
    write(ans);
    return 0;
}

然后来看如何改manacher

manacher是一种线性算法,用来求最长回文子串。不会的看这里:https://www.cnblogs.com/WWHHTT/p/10460258.html

然后改的时候将会问的判断条件改为一个0一个1对应,最后在将所有的中心位于#的p值除以二再相加即可 (因为反对称的字符串长度必须为偶数)

下面给出代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<cmath>
using namespace std;
inline long long rd(){
    long long x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
inline void write(long long x){
    if(x<0) putchar('-');
    if(x>9) write(x/10);
    putchar(x%10+'0');
    return ;
}
char s[1000006];
char c[2000006];
long long n;
long long p[2000006];
int main(){
    n=rd();
    scanf("%s",s+1);
    c[0]='$';
    for(long long i=1;i<=n;i++){
        c[i*2-1]='#';
        c[i*2]=s[i];
    }
    c[2*n+1]='#';
    long long mx=0,id=0;
    long long ans=0;
    for(long long i=1;i<=2*n;i+=2){
        if(mx>i) p[i]=min(p[2*id-i],mx-i);
        else p[i]=1;
        while((c[i+p[i]]==c[i-p[i]]&&c[i-p[i]]=='#')||(c[i+p[i]]=='0'&&c[i-p[i]]=='1')||(c[i-p[i]]=='0'&&c[i+p[i]]=='1')) p[i]++;
        if(i+p[i]>mx) mx=i+p[i],id=i;
        if(i%2) ans+=p[i]>>1;
    }
    /*for(long long i=1;i<=2*n;i++){
        cout<<c[i]<<" ";
    }
    cout<<endl;
    for(long long i=1;i<=2*n;i++){
        cout<<p[i]<<" ";
    }*/
    write(ans);
    return 0;
}

 

posted @ 2019-03-02 11:20  Bruce--Wang  阅读(255)  评论(0编辑  收藏  举报