迴文自動機 模板 bzoj3676【Apio2014】迴文串

題目描述:
考慮一個只包含小寫拉丁字母的字符串s。
我們定義s的一個子串t的“出現值”爲t在s中的出現次數乘以t的長度。請你求出s的所有迴文子串中的最大出現值。

題目分析:(迴文自動機)

某隻問:迴文自動機有什麼用?
另只答:除了把Apio2014迴文串那題變成裸題之外沒什麼卵用了。
……
本蒟蒻:嗯嗯(無腦點頭)

迴文自動機可以識別一個串的所有迴文子串【的一半】。
像Manacher一樣在兩個字符中間插入一個‘ ` ’,這樣可以只寫一個根。
具體的做法就是找到上一個字符爲結尾且把當前要插入的字符插入之後仍然是迴文串的最長迴文串,然後順理成章的接在後面接好啦。
哎,說起來簡單=。=

代碼如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define N 310000
using namespace std;
inline long long Max(long long x,long long y) { return x>y?x:y; }
int n;
long long ans;
char s[N<<1];
namespace Palindrome_Automaton{
    struct Trie{
        Trie *son[27],*fail;
        int dpt,cnt;
        Trie() {}
        Trie(int _):dpt(_){}
    }mempool[N<<1],*C=mempool;
    Trie *root=new (C++) Trie(0),*last=root;
    void extend(int x)
    {
        Trie *p=last;
        while(s[x-2*p->dpt]!=s[x]) 
            p=p->fail;
        if(p->son[s[x]-'`']) 
            last=p->son[s[x]-'`'];
        else
        {
            last=p->son[s[x]-'`']=new (C++) Trie(p->dpt+1);
            for(p=p->fail;p;p=p->fail)
                if(s[x-2*p->dpt]==s[x])
                {
                    last->fail=p->son[s[x]-'`'];
                    break;
                }
            if(!p) last->fail=root;
        }
        last->cnt++;
    }
}
void bfs()
{
    using namespace Palindrome_Automaton;
    static Trie *q[N<<1];
    int i,l=1,r=0;
    q[++r]=root;
    while(l<=r)
    {
        Trie *p=q[l++];
        for(int i=0;i<=26;i++)
            if(p->son[i])
                q[++r]=p->son[i];
    }
    for(i=r;i>1;i--)
    {
        Trie *p=q[i];
        p->fail->cnt+=p->cnt;
        ans=Max(ans,(long long)(p->dpt-1)*p->cnt);
    }
}
int main()
{
    using namespace Palindrome_Automaton;
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=n<<1|1;i;i--)
    {
        if(i&1) s[i]='`';
        else s[i]=s[i>>1];
    }
    for(int i=1;i<=(n<<1|1);i++)
        extend(i);
    bfs();
    cout<<ans<<endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章