1. 程式人生 > >【BZOJ2084】【洛谷P3501】[POI2010]ANT-Antisymmetry(Manache算法)

【BZOJ2084】【洛谷P3501】[POI2010]ANT-Antisymmetry(Manache算法)

異或操作 image font bzoj rdquo 技術 sign ant close

題意描述

  原題:

  技術分享圖片

    一句話描述:對於一個0/1序列,求出其中異或意義下回文的子串數量。


題解

  我們可以看出,這個其實是一個對於異或意義下的回文子串數量的統計,什麽是異或意義下呢?平常,我們對回文的定義是,對於任意$i$,$S[i]=S[n-i+1]$,而我們把相等改為異或操作,那麽,當且僅當$1$與$0$相匹配時,返回值為$1$ 也就是 “真”。

  那麽,我們可以嘗試使用Manache算法來解決。當然,編程時,我們並不必真的去把0/1序列轉換為數字序列,進行異或操作,這樣會給自己增加一波常數(迷),我們構造一個to數組,$to[x]$數組的定義為 對於字符$x$ 我們允許匹配的對應字符,顯然,$to[‘0‘]=‘1‘$,$to[‘1‘]=‘0‘$,特別的$ to[‘\#‘]=‘\#‘ $ $to[‘\$‘]=‘\$‘ $。(此處‘#‘與‘$‘是Manache算法的分隔字符與防止溢出字符,可以自定義)。

  對於代碼:

  

技術分享圖片
 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 const int maxn = 1000010;
 5 typedef unsigned long long ull;
 6 char SS1[maxn],S[maxn],to[500];
 7 int n,len[maxn],tot=1;
 8 int main() {
 9     scanf("%d%s",&n,SS1+1);S[0]=$,S[1]=#;
10     for
(register int i=1;i<=n;++i) S[++tot]=SS1[i],S[++tot]=#; 11 to[1]=0,to[0]=1,to[#]=#,to[$]=$; 12 int pos=1,mx=1;ull ans=0; 13 for(register int i=1;i<=tot;i+=2) { 14 len[i]=(i<mx?std::min(mx-i,len[(pos<<1)-i]):1); 15 while(S[i+len[i]]==to[S[i-len[i]]]) len[i]++;
16 if(len[i]+i>mx) { 17 mx=len[i]+i;pos=i; 18 } 19 ans+=len[i]>>1; 20 } 21 printf("%llu\n",ans); 22 return 0; 23 }
View Code

【BZOJ2084】【洛谷P3501】[POI2010]ANT-Antisymmetry(Manache算法)