1. 程式人生 > 其它 >迴文自動機做法講解

迴文自動機做法講解

技術標籤:字串學習筆記

List

是什麼

簡明地說,

  • 每個點表示某個字首的最長迴文字尾
  • 每條正式的有向邊 a→c→b 表示在 a 兩邊都加上 c 會變成 b
  • 由於一條路徑上的點長度奇偶性相同,所以初始兩個點,0點長度為 0,1點長度為 -1
  • 為了方便判斷邊界,規定fail[0]=1,初始孩子都為0

是不是覺得有很多疑問,沒事,看程式碼

CODE

(眾所周知,英語的多義性相比漢語要小很多)

(解釋得很清晰了,老外都看得懂)

Definitions

char ss[MAXN];// Original String
struct PAM{
	int s[MAXC];// Sons
	int len,siz,fail; 
	//len:	Length of the longest palindrome suffix of the prefix ;
	//siz:	Number of the same substrings that is one's longest palindrome suffix;
	//fail:	ID of the longest palindrome suffix of this suffix ;
	PAM() {memset(s,0,
sizeof(s));len = siz = fail = 0;} PAM(int L,int F) { memset(s,0,sizeof(s));len = L;siz = 0;fail = F; } }pam[MAXN] = {PAM(0,1),PAM(-1,1)}; int las = 0,cnt = 1,Len = 0;// ID of the latest point, Number of the points, The present length int ct[MAXN];// The final number of times that one palindrome substring appears

definition: noun.定義
palindrome: noun.迴文

Init

void rebuild() { // As its name
	pam[0] = PAM(0,1); pam[1] = PAM(-1,1); // In order to be easy in the "getfail" function
	las = 0;cnt = 1;Len = 0; // The same as definitions
	memset(ct,0,sizeof(ct)); // This is also important
}

Important Function

int getfail(int x) {
	while(ss[Len - pam[x].len - 1] != ss[Len]) 
		x = pam[x].fail; 
// The ending is point 1, so it will stop as ss[Len - (-1) - 1] obviously equals to ss[Len].
	return x;
}

Adding Operation

void addpam(int c) { Len ++;
	int cur = getfail(las); 	// First, we are supposed to find its father。
	int now = pam[cur].s[c];	// (The probable point that is equal to the new one)
	if(!now) { 					// Then if we find it distinct, we should continue.
		now = ++ cnt; pam[now] = PAM(); 					// Build a new point ,
		pam[now].len = pam[cur].len + 2; 					// and set the length.
		pam[now].fail = pam[getfail(pam[cur].fail)].s[c]; 	//We should get the "fail",
		pam[cur].s[c] = now; 								// before setting the child.
	} 				// But what if we find that it has appeared before ? Just let it go.
	pam[las = now].siz ++;// Whether it appeared or not, the last step is to change the "siz".
	return ;
}

Necessary Operation

P.S. It’s used in 100% of the calculations.

void addup() {
	for(int i = cnt;i > 1;i --) ct[pam[i].fail] += (ct[i] += pam[i].siz);
	// Add the "siz" to itself, and its suffixs.
	// We can simply do it from "cnt" to 1 since "pam[x].fail < x".
}

兩個推論

  • 一個長為n的字串內本質不同的迴文子串最多n個。
  • 一個字串內每個字首的最長迴文字尾都對應一個迴文子串,且每種本質不同的迴文子串都會出現,即形成滿射

所以迴文自動機裡存了一個字串所有的迴文子串。

相比起比較麻煩的 SAM+Manacher ,它可以更好地解決迴文串匹配等問題。
我的意思是,它比 SAM+Manacher 好打、好除錯,因為我們的一些初始定義解決了大部分邊界問題。