Explanation of the practice of palindrome automata

What is it

To put it succinctly,

  • Each dot represents the longest palindrome suffix of a certain prefix
  • Each formal directed edge a → c → b means that adding c on both sides of a will become b
  • Since the length and parity of the points on a path are the same, so the initial two points, the length of 0 point is 0, and the length of 1 point is -1
  • In order to facilitate the judgment of the boundary, it is stipulated that fail[0]=1, and the initial children are all 0

Do you think there are a lot of questions, it’s okay, look at the code

CODE

(As we all know, the ambiguity of English is much smaller than that of Chinese)

(The explanation is very clear, and foreigners can understand it)

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. definition
palindrome : noun. palindrome

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".
}

Two inferences

  • There are at most n palindrome substrings that are essentially different in a string of length n.
  • The longest palindrome suffix of each prefix in a string corresponds to a palindrome substring, and each palindrome substring with different nature will appear, that is, a surjective .

So the palindromic automaton stores all the palindrome substrings of a string.

Compared with the more troublesome SAM+Manacher, it can better solve problems such as palindrome matching.
What I mean is that it is better than SAM+Manacher and easier to debug, because some of our initial definitions solve most of the boundary problems.

Guess you like

Origin blog.csdn.net/weixin_43960414/article/details/112429699