1. 程式人生 > 實用技巧 >BZOJ-2084 [Poi2010]Antisymmetry(Manacher演算法)

BZOJ-2084 [Poi2010]Antisymmetry(Manacher演算法)

題目描述

  對於一個 \(01\) 字串,如果將這個字串 \(0\)\(1\) 取反後,再將整個串反過來和原串一樣,就稱作反對稱字串。比如 \(00001111\)\(010101\) 就是反對稱的,\(1001\) 就不是。現在給出一個長度為 \(n(1\leq n\leq 5\times 10^5)\)\(01\) 字串,求它有多少個子串是反對稱的。

分析

  將 \(\text{Manacher}\) 演算法判斷迴文的條件改為判斷不相同的字元即可,而且只考慮長度為偶數的反對稱串,因為長為奇數的串不可能反對稱。

程式碼

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char a[N],s[N];
int n,p[N];
bool check(char x,char y)
{
    if(x=='1'&&y=='0')
        return true;
    if(x=='0'&&y=='1')
        return true;
    if(x=='#'&&y=='#')
        return true;
    return false;
}
void Manacher()
{
    s[0]='$';s[1]='#';
    for(int i=0;i<n;i++)
    {
        s[2*i+2]=a[i];
        s[2*i+3]='#';
    }
    int maxright=0,/*ans=0,*/mid=0;
    for(int i=1;i<2*n+1;i=i+2)
    {
        if(i<maxright)
            p[i]=min(p[mid*2-i],maxright-i);
        else
            p[i]=1;
        while(check(s[i-p[i]],s[i+p[i]]))
            p[i]++;
        if(i+p[i]>maxright)
        {
            mid=i;
            maxright=i+p[i];
        }
        //ans=max(ans,p[i]);
    }
}
int main()
{
    cin>>n;
    scanf("%s",a);
    Manacher();
    long long ans=0;
    /*for(int i=0;i<=2*n+1;i++)
        cout<<s[i];
    cout<<endl;
    for(int i=0;i<=2*n+1;i++)
        cout<<p[i]<<" ";
    cout<<endl;*/
    for(int i=1;i<=2*n+1;i=i+2)
        ans=ans+(p[i])/2;
    cout<<ans<<endl;
    return 0;
}