1. 程式人生 > >【bzoj2084】[Poi2010]Antisymmetry

【bzoj2084】[Poi2010]Antisymmetry

rip etc include arc zoj try 分享 答案 dal

2084: [Poi2010]Antisymmetry

Time Limit: 10 Sec Memory Limit: 259 MB
Submit: 1205 Solved: 756
[Submit][Status][Discuss]

Description

對於一個01字符串,如果將這個字符串0和1取反後,再將整個串反過來和原串一樣,就稱作“反對稱”字符串。比如00001111和010101就是反對稱的,1001就不是。
現在給出一個長度為N的01字符串,求它有多少個子串是反對稱的。

Input

第一行一個正整數N (N <= 500,000)。第二行一個長度為N的01字符串。

Output


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

Sample Input

8
11001011

Sample Output

7

hint
7個反對稱子串分別是:01(出現兩次), 10(出現兩次), 0101, 1100和001011

HINT

Source

鳴謝 JZP

題意:

求一個01串中“反回文”子串的個數。“反回文”的定義為str[i]=!str[N-i+1]。

題解:

這道題枚舉回文子串中間位置+二分答案即可AC,但實際上存在一種名為Manacher的線性算法。

我記得我個人解決回文子串問題的復雜度從O(N^3)到O(N^2)再到O(NlogN)不斷進步,這次終於達到理論上的下限了……


(以下圖片全部轉自CSDN某dalao,侵刪)

一般的回文串算法都是枚舉回文串的中心位置然後分奇偶討論。但Manacher算法提供了一種巧妙的方法使得可以將奇偶回文串在一起處理。

具體方法是在原串每兩個字符間插入一個分隔符,再在頭尾分別插入一個分隔符,分隔符要求不在原串中出現。如下:

技術分享圖片

然後我們考慮設len[i]表示在新串中以i為中心的回文串向右延伸的長度。如下:

技術分享圖片

接著歸納求解,假設我們已經求出了len[1~i-1]的值,現在要求len[i],記其中向右延伸到的位置最遠的len[Po]為P(與擴展kmp神似),有如下幾種情況:

若i<=P,那麽找到i相對於po的對稱位置j,如果len[j]<=P-i,如圖:

技術分享圖片

此時由對稱性可得len[i]=len[j]。

如果len[j]>P-i,如圖:

技術分享圖片

此時len[i]至少為P-i,由於i右邊的字符沒有被匹配過,我們需要依次匹配並更新Po與P。

若i>P,如圖:

技術分享圖片

此時之前處理的信息對i沒有什麽用,我們仍然需要依次匹配並更新。

Manacher算法每次新匹配k個位置,匹配完即退出。每個位置會被計算且僅被計算一次,所以該算法的復雜度是線性的。

這道題即是Manacher算法的模板,只需要把匹配運算修改一下即可。

代碼:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;
#define MAXN 500005
#define MAXM 500005
#define INF 0x7fffffff
#define ll long long

char tp[MAXN],str[MAXN<<1|1];
ll len[MAXN<<1|1];
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())
        if(c==-)
            f=-1;
    for(;isdigit(c);c=getchar())
        x=x*10+c-0;
    return x*f;
}

inline void solve(ll N){
    for(ll i=1;i<=N;i++)
        str[(i<<1)-1]=#,str[i<<1]=tp[i];
    str[N<<1|1]=#;str[0]=!;str[(N<<1|1)+1]=?;
    return;
}
inline bool check(char x,char y){return (x==#&&y==#)||(((x-0)^(y-0))==1);}
inline ll Manacher(ll N){
    ll pos=0,lag=0,ans=0;
    for(ll i=1;i<=N;i++){
        len[i]=(i<lag)?min(lag-i+1,len[(pos<<1)-i]):0;
        while(check(str[i+len[i]],str[i-len[i]])) len[i]++;
        if(i+len[i]-1>lag) lag=i+len[i]-1,pos=i;
        ans+=len[i]>>1;//cout<<len[i]<<endl;
    }return ans;
}

int main(){
    ll N=read();cin>>tp+1;solve(N);
    printf("%lld\n",Manacher(N<<1|1));
    return 0;
}

【bzoj2084】[Poi2010]Antisymmetry