zoukankan      html  css  js  c++  java
  • 回文自动机简述

    回文自动机简述

    其实不算毒瘤,与AC自动机比较相似。

    定义

    回文自动机是有两棵树的森林,其处理了一个字符串中以某字符结尾的全部回文串。

    其中一棵是长度为偶数的回文串集合,根为0,另一棵是长度为奇数的回文串集合,根为1。

    对于串abbaabba的回文自动机如下

    从网上随便找了张图

    img

    实现方法

    回文串在回文自动机中实际上只存了一半。

    如上图的7号节点,代表回文串baab,3号节点代表回文串b

    理解自动机的关键就是理解(fail)指针,回文自动机的(fail)定义如下

    对于节点i

    回文自动机的fail,为i所代表的回文串的最长回文真后缀的那个点。

    那么当我们匹配i+1失配时,我们可以跳到fail[i]然后比较fail所代表的回文串的前一个字符是否与i+1相等。

    如此递归,直到跳到根,那么i+1的回文长度 len[i+1]=1否则len[i+1]=len[匹配到的fail]+1;

    如何构建fail指针?

    就如同AC自动机一样,我们考虑用上一个回文串的fail来构建这个新插入点的fail

    我们先判断i这个字符是不是等于上一个串的fail串前一个,如果是就匹配成功。

    否则继续沿着fail指针跳,边跳边判断就可以了。

    (Problem List)

    (1.)洛谷P1872回文串计数

    (2.)[APIO2014]回文串

    (Solution)

    (1.)板子题。

    /*
    @Date    : 2019-07-15 16:33:37
    @Author  : Adscn (adscn@qq.com)
    @Link    : https://www.cnblogs.com/LLCSBlog
    */
    #include<bits/stdc++.h>
    using namespace std;
    #define IL inline
    #define RG register
    #define gi getint()
    #define gc getchar()
    #define File(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
    IL int getint()
    {
    	RG int xi=0;
    	RG char ch=gc;
    	bool f=0;
    	while(ch<'0'||ch>'9')ch=='-'?f=1:f,ch=gc;
    	while(ch>='0'&&ch<='9')xi=(xi<<1)+(xi<<3)+ch-48,ch=gc;
    	return f?-xi:xi;
    }
    IL void pi(int k,char ch=0)
    {
    	if(k<0)k=-k,putchar('-');
    	if(k>=10)pi(k/10,0);
    	putchar(k%10+'0');
    	if(ch)putchar(ch);
    }
    #define int long long
    const int N=2e4+7;
    char a[N];
    long long l[N];
    struct PAM{
    	struct node
    	{
    		int c[26];
    		int fail,len;
    		node(){fail=len=0;}
    	}t[N];
    	int num[N];
    	char S[N];
    	int n,cnt;
    	explicit PAM()
    	{
    		n=0;
    		cnt=1;
    		t[1].len=-1;
    		t[0].fail=1;
    	}
    	inline int getfail(int x)
    	{
    		while(S[n]^S[n-t[x].len-1])x=t[x].fail;//这就是为什么t[1].len要等于-1
    		return x;
    	}
    	inline int insert(int x)
    	{
    		S[++n]=(x-='a');
    		int cur=getfail(last);
    		if(!t[cur].c[x])
    		{
    			int nw=++cnt;
    			t[nw].fail=t[getfail(t[cur].fail)].c[x];
    			t[nw].len=t[cur].len+2;
    			t[cur].c[x]=nw;
    			num[nw]=num[t[nw].fail]+1;
    		}
    		last=t[cur].c[x];
    		//++t[last].cnt;
    		return num[last];
    	}
    	int last;
    }x,y;
    long long ans;
    signed main(void)
    {
    	cin>>(a+1);
    	int len=strlen(a+1);
    	for(int i=1;i<=len;++i)l[i]=l[i-1]+x.insert(a[i]);
    	for(int i=len;i>1;--i)ans+=1ll*l[i-1]*y.insert(a[i]);
    	cout<<ans;
    	return 0;
    }
    
    

    (2.)更加板子是smg

    在fail树上累加cnt即可。

    注意当我们将fail树上的儿子的贡献累加到父亲时,我们不必进行dfs

    因为fail[x]<x,所以我们直接从n枚举到1就可以了

    /*
    @Date    : 2019-07-15 17:54:11
    @Author  : Adscn (adscn@qq.com)
    @Link    : https://www.cnblogs.com/LLCSBlog
    */
    #include<bits/stdc++.h>
    using namespace std;
    #define IL inline
    #define RG register
    #define gi getint()
    #define gc getchar()
    #define File(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
    IL int getint()
    {
    	RG int xi=0;
    	RG char ch=gc;
    	bool f=0;
    	while(ch<'0'||ch>'9')ch=='-'?f=1:f,ch=gc;
    	while(ch>='0'&&ch<='9')xi=(xi<<1)+(xi<<3)+ch-48,ch=gc;
    	return f?-xi:xi;
    }
    IL void pi(int k,char ch=0)
    {
    	if(k<0)k=-k,putchar('-');
    	if(k>=10)pi(k/10,0);
    	putchar(k%10+'0');
    	if(ch)putchar(ch);
    }
    const int N=3e5+7;
    struct PAM{
    	int n;
    	int fail[N],cnt[N],c[N][26],len[N];
    	int S[N];
    	int last,tot;
    	PAM()
    	{
    		tot=1,n=0;
    		fail[0]=1,len[1]=-1;
    	}
    	inline int getfail(int x)
    	{
    		while(S[n]^S[n-len[x]-1])x=fail[x];
    		return x;
    	}
    	inline void add(int x)
    	{
    		S[++n]=(x-='a');
    		int cur=getfail(last);
    		if(!c[cur][x])
    		{
    			int nw=++tot;
    			fail[nw]=c[getfail(fail[cur])][x];
    			len[nw]=len[cur]+2;
    			c[cur][x]=nw;
    		}
    		++cnt[last=c[cur][x]];
    	}
    	void init()
    	{
    		static char s[N];
    		cin>>(s+1);
    		int Siz=strlen(s+1);
    		for(int i=1;i<=Siz;++i)add(s[i]);
    	}
    }x;
    int main(void)
    {
    	x.init();
    	long long ans=0;
    	for(int i=x.tot;i;--i)x.cnt[x.fail[i]]+=x.cnt[i],ans=max(ans,1ll*x.cnt[i]*x.len[i]);
    	cout<<ans;
    	return 0;
    }
    
    
  • 相关阅读:
    Iscroll滑动无效
    原生js 无缝滚动组件
    原生 js dialog弹窗组件
    html5 历史管理
    html5拖拽属性
    highcharts 数据图设置X轴间隔显示效果
    highcharts柱状图含有正负柱设置不同颜色的方法
    移动端滑动插件 swiper
    千分位添加和去掉方法
    dubbo常用类和路径
  • 原文地址:https://www.cnblogs.com/LLCSBlog/p/11190127.html
Copyright © 2011-2022 走看看