• 回文自动机


    回文自动机也叫做回文树

    由名字可知:它是一棵树

    树有什么性质?

    节点个数为n,一个点只有一条出边(一个父亲);

    我们把符合树的性质的回文字典树叫做回文自动机;

    由树的性质大致可以推出:回文自动机的时间复杂度是线性的!(废话,不然要你有何用?)

    首先,由于回文串的性质,回文自动机的最大的一个特点就是他的起始节点有两个,本质是分开考虑奇串和偶串。

    在回文自动机的上面,存在着两种边:转移边和后缀边;

    若回文串 S 有一条 ch 的转移边到 S′ ,说明存在一个回文串 S两端各增加1个字符 ch ,将形成回文串 S′

    特殊的,对于 −1根的转移边,表示单个字符表示的回文串,如 a

    若回文串 S 有一条后缀边连接到 S′ ,说明 S′S最大回文串后缀(不含 S自身)。

    对于 0根和 −1根,其后缀边都连向-1根,为的是统一奇串和偶串。

    构造回文自动机的方法采用增量法;

    char s[2000010];
    class node{
    	public:
    	int ch[29];
    	int link;
    	int len,cnt;
    }pam[500010];
    int size,root0,root1,last;
    int ans[500010];
    class node2{
    	public:	
    	void set(){
    		size=0; root0=size++; root1=size++;
    		last=root1;
    		pam[root0].link=root1; pam[root0].len=0;
    		pam[root1].link=root1; pam[root1].len=-1;
    	}			
    	void insert(int to,int pos){		
    		register int u=last;
    		while(s[pos-pam[u].len-1]!=s[pos]) u=pam[u].link;		
    		if(!pam[u].ch[to]){
    			register int neww=++size,v=pam[u].link;
    			pam[neww].len=pam[u].len+2;					
    			while(s[pos-pam[v].len-1]!=s[pos]) v=pam[v].link;				
    			pam[neww].link=pam[v].ch[to];
    			pam[u].ch[to]=neww;
    			ans[neww]=ans[pam[neww].link]+1;
    		}
    		last=pam[u].ch[to];
    	}
    }PAM;
    

    在有些题目中,我们呢要快速知道一个字符串的某个特殊fail指针(就是这个fail指针的字符串的长度要小于等于该字符串的一半),叫做trans,trans可以通过找fail的时候得到,具体的实现可以参考下面的代码;

    char s[600010];
    int n;
    class node1{
        public:
        int len,link,trans;
        int ch[27];
    }pam[600010];
    int last,size,root1,root2;
    int cnt[600010];
    class node{
        public:
        void set(){
            size=0; root1=size++; root2=size++;
            last=root2;
            pam[root1].len=0; pam[root1].link=root2;
            pam[root2].len=-1; pam[root2].link=root2;
        }
        void add(char c,int pos){
            int u=last;
            while(s[pos-pam[u].len-1]!=s[pos]) u=pam[u].link;
            if(!pam[u].ch[c-'a']){
                int neww=size++;
                int v=pam[u].link;
                pam[neww].len=pam[u].len+2;
                while(s[pos-pam[v].len-1]!=s[pos]) v=pam[v].link; 
                pam[neww].link=pam[v].ch[c-'a'];
                pam[u].ch[c-'a']=neww;
                if(pam[neww].len<=2){
                    pam[neww].trans=pam[neww].link;
                }
                else{
                    int tmp=pam[u].trans;
                    while(s[pos-1-pam[tmp].len]!=s[pos]||(pam[tmp].len+1)*2>=pam[neww].len){
                        tmp=pam[tmp].link;              
                    }
                    pam[neww].trans=pam[tmp].ch[c-'a'];
                }
                
            }
            last=pam[u].ch[c-'a'];
        }
    }PAM;
  • 相关阅读:
    【C++】不同含义new和delete
    苹果Lion 操作系统还没到成熟时 不及格的程序员
    视图旋转方式 不及格的程序员
    来电归属地 黑名单‘s 不及格的程序员
    dispatch_async 线程分发注意事项. 不及格的程序员
    IEEE printf specification 不及格的程序员
    What is the new iPad retina display? 不及格的程序员
    图形设备接口的起源 不及格的程序员
    windows xp 遭遇administrator账号劫持 不及格的程序员
    What does "not supported" mean? 不及格的程序员
  • 原文地址:https://www.cnblogs.com/kamimxr/p/12063653.html
Copyright © 2020-2023  润新知