1. 程式人生 > >BZOJ3676: [Apio2014]回文串

BZOJ3676: [Apio2014]回文串

pro sin mil fin namespace find 簡單 article struct

【傳送門:BZOJ3676


簡要題意:

  給出一個字符串,每一個回文子串的價值為這個回文子串出現的次數*回文子串的長度,求出價值最大的回文子串的價值


題解:

  %%%又是一道新算法

  本來想用AC自動機+馬拉車搞一下的,結果不會

  hzwer大佬的題解用了後綴自動機+馬拉車

  但是後綴自動機太難了

  這時引出我們的新算法——回文樹

  算法詳解,以及推薦代碼風格

  簡單來說,回文樹可以解決以下問題:

  1.求串S前綴0~i內本質不同回文串的個數(兩個串長度不同或者長度相同且至少有一個字符不同便是本質不同)
  2.求串S內每一個本質不同回文串出現的次數


  3.求串S內回文串的個數(其實就是1和2結合起來)
  4.求以下標i結尾的回文串的個數

  幾乎很多回文問題均可解決

  而這道題就是回文樹裸題了,直接在加入節點是順便統計一下個數,最後按fail邊自下而上更新一下即可


參考代碼:

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
typedef long long LL;
struct
Tree { int len,fail,son[26]; }tr[310000]; char st[310000]; int len,last,tot,cnt[310000]; void ready() { tr[0].len=0;tr[1].len=-1; tr[0].fail=1; tot=1;last=0; } int findfail(int i,int j) { while(st[i-tr[j].len-1]!=st[i]) j=tr[j].fail; return j; } void add(int i,int x) {
int j=findfail(i,last); if(tr[j].son[x]==0) { tr[++tot].len=tr[j].len+2; tr[tot].fail=tr[findfail(i,tr[j].fail)].son[x]; tr[j].son[x]=tot; } last=tr[j].son[x]; cnt[last]++; } LL getans() { LL ans=0; for(int i=tot;i>1;i--) { cnt[tr[i].fail]+=cnt[i]; ans=max(ans,LL(tr[i].len)*LL(cnt[i])); } return ans; } int main() { scanf("%s",st+1); len=strlen(st+1); ready(); for(int i=1;i<=len;i++) add(i,st[i]-a); printf("%lld",getans()); }

BZOJ3676: [Apio2014]回文串