1. 程式人生 > 實用技巧 >初學回文自動機

初學回文自動機

前言

迴文自動機,據說是解決迴文問題的利器。

最近剛好遇到一道迴文問題,加上正好閒著沒事幹,就來學了學。

感覺板子還是非常簡潔的,容易記憶。

節點資訊

與一般自動機類似,定義一個節點的資訊。

一般包括表示長度\(fail\)指標後繼節點,當然視具體題目還要維護一些特殊資訊。

但也要注意迴文自動機與一般自動機的區別,就是它的後繼\(x\)表示在當前串左右兩邊各加上一個字元\(x\)(要保證每個節點對應的串是迴文串)。

奇根與偶根

考慮迴文分為奇迴文和偶迴文,因此迴文自動機一個特殊的地方就是,它有兩個根:奇根以及偶根。

分別設定它們的初始資訊:

節點編號 表示長度 \(fail\)指標
奇根 \(1\) \(-1\) 奇根偶根皆可
偶根 \(0\) \(0\) 奇根

\(fail\)指標

迴文串和一般的字串相比要相對複雜,因此它的\(fail\)指標也不能和其他自動機一樣直接呼叫,而需要為它寫一個專門的函式。

首先,一個節點的\(fail\)指標所指向的,是該回文串的最長迴文字尾所對應的節點。

但在具體使用時,由於迴文串的後繼節點要求在串兩側各加上一個字元,因此一個節點的\(fail\)指標並不一定在任何時候都能實現擴充套件。

因此,我們需要不斷跳\(fail\),直至找到一個合法的節點,滿足它可以向兩側擴充套件。

這一過程可以定義為一個函式\(Fail(x,id)\)

,具體實現詳見程式碼。

插入

插入一個新的位置\(id\),首先我們要通過\(t=Fail(lst,id)\)得到第一個可擴充套件位置。

如果\(t\)已經有對應兒子,就不需要操作了;否則,我們新建一個節點,節點長度就是\(t\)的長度加\(2\)(最後一次重複,迴文串每次在串兩側各加上一個字元),而\(fail\)指標就是繼續上跳,得到\(Fail(fail[t],id)\)

這一過程是非常簡短,也非常簡單的。

程式碼(板子題

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
using namespace std;
int n;char s[N+5];
class PalindromeAutomation
{
	private:
		int Nt,lst;struct node {int L,V,F,S[30];}O[N+5];
		I int Fail(RI x,CI id) {W(s[id-O[x].L-1]^s[id]) x=O[x].F;return x;}//根據實際情況找到合法的節點
	public:
		I PalindromeAutomation() {O[O[0].F=Nt=1].L=-1;}//初始化節點資訊
		I int Ins(CI id)
		{
			RI x=s[id]&31,t=Fail(lst,id),o;!O[t].S[x]&&(o=++Nt,//如果沒有對應兒子,新建節點
				O[o].L=O[t].L+2,O[o].V=O[O[o].F=O[Fail(O[t].F,id)].S[x]].V+1,O[t].S[x]=o);//計算當前點資訊
			return O[lst=O[t].S[x]].V;//返回
		}
}P;
int main()
{
	RI i,lst=0;for(scanf("%s",s+1),n=strlen(s+1),i=1;i<=n;++i)
		s[i]=(s[i]-97+lst)%26+97,printf("%d%c",lst=P.Ins(i)," \n"[i==n]);return 0;
}