1. 程式人生 > >省選演算法學習-迴文自動機 && 迴文樹

省選演算法學習-迴文自動機 && 迴文樹

前置知識

首先你得會manacher,並理解manacher為什麼是對的(不用理解為什麼它是$O(n)$,這個大概記住就好了,不過理解了更方便做$PAM$的題)

什麼是迴文自動機?

迴文自動機(Palindrome Automaton),是一類有限狀態自動機,能識別一個字串的所有迴文子串

它可簡化構建出迴文樹

迴文自動機的構造

網上資料很多,不拿出來一步步說了,說一下陣列意義、放個板子

$len[u]$表示節點$u$代表的迴文串的長度

$ch[u][c]$代表在$u$的迴文串兩端新增字元$c$得到的新迴文串節點

$fail[u]$表示節點$u$的迴文串的最長的迴文字尾所在的節點

char s[300010];
int n;
//這個板子裡面的num表示當前節點的迴文串出現了幾遍
namespace pam{
    int fail[300010],num[300010],len[300010],ch[300010][26],last,cnt;
    inline int newnode(int w){len[++cnt]=w;return cnt;}
    void init(){
        s[0]=-1;cnt=-1;fail[0]=1;last=0;
        newnode(0);newnode(-1);//插入兩個根節點,設定fail
    }
    inline int getfail(int cur,int pos){
        while(s[pos-len[cur]-1]!=s[pos]) cur=fail[cur];//跳fail
        return cur;
    }
    void insert(int x){
        int c=(s[x]-'a'),cur=getfail(last,x);
        if(!ch[cur][c]){//新建節點
            int now=newnode(len[cur]+2);
            fail[now]=ch[getfail(fail[cur],x)][c];
            ch[cur][c]=now;
        }
        num[last=ch[cur][c]]++;
    }
}

幾個容易寫錯的點:

fail初始化的時候,如果多組資料並且在newnode裡面初始化資訊,那麼在init的時候記得把fail放到newnode後面

插入的時候函式傳進去的是位置

newnode是len[cur]+2不是+1

一些拓展

節點上

首先顯然可以統計這個節點的迴文串出現次數

統計次數的時候還要加上$fail$樹上子樹內的所有節點的出現次數

對於一類迴文串擁有某個和其$fail$樹有關的性質的題目,可以記錄一個$trans$,和跳$fail$一樣跳,最後用$bfs$來做$dp$或者遞推

關於求最小回文串分解

這個問題是問可以把一個字串分解成最少多少個迴文串

解決的方法:

考慮一個顯然的$dp$:$dp[i]=dp[j-1]+1 (s[i...j]=palindrome)$

記錄一個$anc[u]$

如果$len[u]-len[fail[u]]==len[fail[u]]-len[fail[fail[u]]]$,那麼$anc[u]=anc[fail[u]]$

否則$anc[u]=u$

對於$u$的所有跳上去的$anc$集合$S$,我們發現,這個集合中的元素構成一個等差數列,相鄰的兩項差代表一種從當前點前面遞推到當前點的迴文串長度

對於每個迴文樹節點記錄$tmp[u]=min(tmp[i-len[anc[u]],tmp[fail[u]])$,然後用這個$tmp[u]+1$來更新當前節點的$dp$,然後$u=fail[anc[u]]$往上跳,直到到達根

證明網上有論文,這裡放個程式碼

inline void insert(int x){
    int c=s[x]-'a',cur=getfail(last,x);
    val[x]=1e9;
    if(!ch[cur][c]){
        int now=newnode(len[cur]+2);
        fail[now]=ch[getfail(fail[cur],x)][c];
        ch[cur][c]=now;
        anc[now]=((fail[now]>1&&len[now]-len[fail[now]]==len[fail[now]]-len[fail[fail[now]]])?anc[fail[now]]:now);
    }
    last=ch[cur][c];
    for(cur=ch[cur][c];cur>1;cur=fail[anc[cur]]){
        tval[cur]=val[x-len[anc[cur]]];
        tpos[cur]=x-len[anc[cur]];
        if(anc[cur]!=cur&&tval[fail[cur]]<tval[cur]) tval[cur]=tval[fail[cur]],tpos[cur]=tpos[fail[cur]];
        if(val[x]>tval[cur]+1) val[x]=tval[cur]+1,pos[x]=tpos[cur];
    }
}