迴文自動機做法講解
阿新 • • 發佈:2021-01-11
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 好打、好除錯,因為我們的一些初始定義解決了大部分邊界問題。