zoukankan      html  css  js  c++  java
  • [SHOI2011]双倍回文

    题目

    回文自动机的板子题啦

    还是先胡扯几句回文自动机是如何构建的吧,这个东西比(SAM)可是简单多了

    回文自动机就是一棵树,树上每一个节点表示了一个回文串,由于一个字符串本质不同的回文子串个数不会超过字符串串长,所以回文树上总结点个数是线性的

    这个回文自动机由下面几个东西构成

    1. (fail[x]),就是(x)这个节点的失配指针,指向(x)表示的回文串的最长回文后缀

    2. (son[x][c]),就是在(x)表示的回文串左右两边加上一个字符(c)得到的回文串

    3. (len[x]),就是(x)表示的回文串的长度

    (SAM)一样,回文自动机也是动态往自动机里插入一个字符

    类似马拉车,我们插入(i)这个字符只会更新在(i)位置结束的最长回文子串

    (i-1)这个前缀的最长回文后缀是(f),我们要插入的字符是(c),如果(k)前面恰好也有一个(c),我们就可以直接插入了

    如果前面不是(c),我们就跳一下(fail[f])看看最长的回文后缀前面是否有(c),不然就继续跳,直到有(c)为止

    设我们跳到的位置为(f),如果(f)(c)这个转移,说明这个回文后缀在之前已经出现过了,我们没有必要再插入了

    如果没有(c)的转移,我们就需要插入一个节点了

    设这个节点是(p),显然(len[p]=len[f]+2),因为(p)(f)左右两边各接上一个(c)得来的

    之后考虑(p)(fail)(fail)必须是一个真后缀,所以我们跳一下使得(k=fail[f]),之后继续跳(fail)使得(k)前面有一个(c)就好了,我们的(fail[p]=son[k][c])

    最后记得更新一下当前(i)前缀的最长回文后缀是(son[f][c])

    inline void ins(int c,int n) {
    	int f=lst;
    	while(S[n-len[f]-1]!=S[n]) f=fa[f];
    	if(!son[f][c]) {
    		int p=++cnt,k=fa[f];len[p]=len[f]+2;
    		while(S[n-len[k]-1]!=S[n]) k=fa[k];
    		fa[p]=son[k][c];son[f][c]=p;
    	}
    	lst=son[f][c];
    }
    

    这就是回文树插入的板子啦

    实现的时候有几个小(trick),就是这一句

    len[1]=-1;fa[0]=1;cnt=1;
    

    我们给回文树搞了两个根节点,(0)代表偶回文串,(1)表示奇回文串,我们发现(fa[0]=1),这样我们一直匹配不上的时候就会跳到(1),显然(S[n]+1-1=S[n]),于是我们就强行配上了

    至于这道题,我们首先发现我们要求的串肯定是一个回文串,且这个回文串可以被分成(4)等份,于是(len\%4=0)

    之后由于这个回文串还能被分成回文的两部分,所以这个回文串必然有一个长度为(len/2)的回文后缀

    这不动物园吗

    我们把(fail)树建出来,之后在上面瞎搞就好了

    代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define re register
    #define LL long long
    const int maxn=5e5+5;
    struct E{int v,nxt;}e[maxn];
    int n,lst,num,cnt,head[maxn],ans,to[maxn];
    char S[maxn];
    int son[maxn][26],fa[maxn],len[maxn];
    inline void add(int x,int y) {
    	e[++num].v=y;e[num].nxt=head[x];head[x]=num;
    }
    inline void ins(int c,int n) {
    	int f=lst;
    	while(S[n-len[f]-1]!=S[n]) f=fa[f];
    	if(!son[f][c]) {
    		int p=++cnt,k=fa[f];len[p]=len[f]+2;
    		while(S[n-len[k]-1]!=S[n]) k=fa[k];
    		fa[p]=son[k][c];son[f][c]=p;add(fa[p],p);
    	}
    	lst=son[f][c];
    }
    void dfs(int x,int t) {
    	while(x!=t&&len[to[t]]*2<=len[x]&&to[t]!=x) t=to[t];
    	if(x!=t&&len[x]%4==0&&(len[t]<<1)==len[x]&&len[x]>ans) ans=len[x]; 
    	for(re int i=head[x];i;i=e[i].nxt) 
    		to[x]=e[i].v,dfs(e[i].v,t);
    }
    int main() {
    	scanf("%d",&n);scanf("%s",S+1);
    	for(re int i=0;i<=n;i++) S[i]-='a';
    	len[1]=-1;fa[0]=1;cnt=1;add(1,0);
    	for(re int i=1;i<=n;i++) ins(S[i],i);
    	dfs(1,1);printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    git笔记
    初试linux,cp、rm、mv、file、umask等命令粗略使用方法
    Linux的rwx
    nano的简单笔记
    windows10访问ftp中文乱码怎么办?
    Windows 将FTP 映射到本地文件夹 --简化操作
    计算地址掩码
    Can't connect to MySQL server on localhost (10061)解决方法
    微信小程序开发入门学习(2):小程序的布局
    微信小程序开发入门学习(1):石头剪刀布小游戏
  • 原文地址:https://www.cnblogs.com/asuldb/p/10763647.html
Copyright © 2011-2022 走看看