迴文自動機(PAM)總結

迴文自動機是接受一個字符串的所有迴文子串的自動機。
迴文自動機中每個點代表原串的一個迴文子串。
維護兩種指針:\(trans\)\(fail\)
\(x\)\(trans[x][c]\) 指針指向在這個點代表迴文串兩端同時加字符 \(c\) 後得到的迴文串。
\(fail\) 指針指向這個點所代表的迴文串的一個最長的迴文串後(前)綴。
構造:增量法。

void insert(int l,int r,int c)
{
    while(r-len[zr]-1<l||sz[r-len[zr]-1]!=c)
        zr=fa[zr];
    if(trs[zr][c])//存在
    {
        zr=trs[zr][c];
        return;//直接返回
    }
    int w=trs[zr][c]=++sl/*新建節點*/,t=fa[zr];
    while(t&&(trs[t][c]==0||r-len[t]-1<l||sz[r-len[t]-1]!=c))//尋找fail(注意)
        t=fa[t];
    if(trs[t][c])fa[w]=trs[t][c];
    else fa[w]=2;//指向偶根
    len[w]=len[zr]+2;zr=w;
}

細節:分爲奇樹,偶樹,接受奇迴文串,偶迴文串。
設奇樹根爲1,偶樹根爲2。
將節點1的長度設爲-1。節點2的長度設爲0,fail設爲1。
初始時的last設爲1。
若沒有fail指針,則fail指向2(偶根)。

int r0=2,r1=1;
fa[r0]=r1;zl=1;sl=2;
len[r0]=0;len[r1]=-1;

迴文樹的匹配類似AC自動機。

int w=1;//從1開始
for(int i=l;i<=r;i++)
{
    while(trs[w][sz[i]]==0||i-len[w]-1<l||sz[i-len[w]-1]!=sz[i])//注意
        w=fa[w];//走fail指針
    w=trs[w][sz[i]];
    qz[i]=w;
}

迴文樹具有性質:正反串的迴文樹是相同的。
因爲每個節點都是迴文,反轉後不變,因此他們的trs,fail都是相同的。
這個性質,使得迴文樹可以支持在兩端插入字符。
我們可以維護頭尾兩個 last 位置,然後在一個迴文自動機中插入。
注意當整個串是一個迴文串時需要把兩個 last 都賦爲這個迴文串

if(len[zr]==r-l+1)zl=zr;

類似後綴自動機,迴文自動機也可以定義right集合,求出迴文串的出現次數。
將每次添加後的 last 設爲紅點。
在 fail 構成的樹上,一個節點子樹中紅點個數就是它的出現次數。

這個也支持添加字符:
先離線讀入所有添加字符操作,並記錄每次的 last 。
對於每次操作,將last到根路徑上都增加1即可。

在兩端插入字符時,此法也是適用的。

定位子串可以倍增,具體方法類似後綴自動機:
先找到右端點代表的前綴,再按照長度倍增。

int getwzl(int l,int r)
{
    int cd=r-l+1,u=qz[r];
    if(len[u]<=cd)//注意此處
        return u;
    for(int i=17;i>=0;i--)
    {
        if(len[bz[i][u]]>cd)
            u=bz[i][u];
    }
    return fa[u];
}

定位到的是這個子串的最長迴文後(前)綴。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章