zoukankan      html  css  js  c++  java
  • 回文自动机 (PAM,Palindrome Automaton)

    回文自动机 (PAM,Palindrome Automaton)

    如果学习了( ext{AC})自动机和后缀自动机(( ext{SAM})),那么这个冷门算法其实非常简单

    约定:原字符串为(S),长度为(|S|)

    结构介绍

    自动机节点意义: ( ext{PAM})没有复杂的结构,每个节点对应了一种回文子串,节点个数(leq |S|+2)

    自动机的转移:( ext{PAM})( ext{AC})自动机一样,有失配指针(fail)和匹配数组(nxt)

    (fail_i)即是(i)的后缀的最长状态,(i)(fail_i)的边构成了一棵树,但是这棵树有着两个根节点(?),分别对应着长度为奇数/偶数的回文子串

    每个转移(nxt_{i,j})意味着在当前状态(i)的串两边增加字符(j)

    但是由于( ext{PAM})的构造是一个在线算法,所以如果想要像( ext{AC})自动机一样每次转移直接访问(nxt),需要结束后遍历结构

    构造

    为了便于访问,设偶数/奇数根分别为(0,1),每个节点存储一个当前状态的长度(len)

    特别的,(len_0=0,len_1=-1)(便于让所有的串都满足(len_{nxt_{i,j}}=len_i+2))

    同时让空串对应奇数根节点,把偶数根连向奇数,即(fail_0=1),这样就只有一个根节点了

    为了在线构造方便,( ext{PAM})需要实现一个匹配函数( ext{Find}(x,y)),即在当前(x)状态找到下一个位置(S_y)的匹配状态,如果失配则返回奇数根(1)

    int Find(int x,int y){
        while(s[y]!=s[y-len[x]-1]) x=fail[x]; // 如果失配到了x=1,那么必然有s[y]=s[y]
        return x;
    }
    

    增加一个节点(S_i=c)

    首先找到一个最长的匹配,设当前前缀最长的回文后缀对应的状态为(now),则直接为(now)匹配(S_i)即可

    然后是新建状态(如果当前的回文子串还未出现过)

    ( ext{AC})自动机类似,访问(fail)树上最近的匹配即可得到这个点的(fail)

    需要注意的点是,因为当前节点可以是根节点,寻找(fail)必须在新建转移(nxt_{now,c})之前进行,否则可能找到的(fail)是自己

    void Extend(int i,int c){
        now=Find(now,i);
        if(!nxt[now][c]) {
            fail[++cnt]=nxt[Find(fail[now],i)][c];
    		len[nxt[now][c]=cnt]=len[now]+2;
        }
        now=nxt[now][c];
    }
    

    模板代码如下:

    char s[N];
    struct Palindrome_Automaton{
    	int len[N],fail[N],nxt[N][26],now,cnt;
    	void Init(){ 
            rep(i,0,cnt) memset(nxt,fail[i]=0,104);
    		s[now=0]=len[1]=-1;
    		fail[0]=fail[1]=cnt=1;
    	}
    	int Find(int x,int y){ 
    		while(s[y-len[x]-1]!=s[y]) x=fail[x];
    		return x;
    	}
    	void Extend(int i,int c){
    		now=Find(now,i);
       		if(!nxt[now][c]) {
    			fail[++cnt]=nxt[Find(fail[now],i)][c];
    			len[nxt[now][c]=cnt]=len[now]+2;
        	}
        	now=nxt[now][c];
    	}
    };
    
    
    

    拓展:回文串与( ext{Border})

    关于( ext{Border})

    推论1:回文串的( ext{Border})也是回文串

    若有回文串(S)的一个( ext{Border} :T),则(S_{1,|T|}=S_{|S|-|T|+1,|S|}=reverse(S_{1,|T|}))

    (T)也是一个回文串

    推论2:遍历回文自动机的(fail)链,能得到当前串的所有( ext{Border})(基于推论1得到)

    结合( ext{kmp,AC})( ext{Border})的关系能够有更好的理解

  • 相关阅读:
    javascript 获取鼠标在盒子中的坐标
    jquery中clientY, pageY, screenY的区别,最后三张图一目了然
    javascript 小清新颜色翻页效果
    javascript 缓动返回顶部案例
    原生js轮播图实现
    javascript Math对象
    javascript 获取节点元素的封装
    javascript 转换大小写字母
    2017 ACM-ICPC 亚洲区(青岛赛区)网络赛 1010
    2017 ACM-ICPC 亚洲区(青岛赛区)网络赛 1009
  • 原文地址:https://www.cnblogs.com/chasedeath/p/13396957.html
Copyright © 2011-2022 走看看