zoukankan      html  css  js  c++  java
  • 【BZOJ2342】双倍回文(回文树)

    【BZOJ2342】双倍回文(回文树)

    题面

    BZOJ

    题解

    构建出回文树之后
    (fail)树上进行(dp)
    如果一个点代表的回文串长度为(4)的倍数
    并且存在长度为它的一半的回文后缀
    那么就是可行的
    如何维护长度是一半的回文后缀?
    (fail)树上的父亲一定包括了它的所有的回文后缀
    因此在(fail)树上(dfs),同时记录一下每个长度的回文出现的次数
    这样访问到一个节点就可以直接检查了

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    using namespace std;
    #define ll long long
    #define RG register
    #define MAX 520000
    inline int read()
    {
        RG int x=0,t=1;RG char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        if(ch=='-')t=-1,ch=getchar();
        while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
        return x*t;
    }
    int n,ans;
    char ch[MAX];
    struct Line{int v,next;}e[MAX];
    int h[MAX],cnt=1;
    inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
    struct Palindromic_Tree
    {
    	struct Node
    	{
    		int son[26];
    		int ff,len;
    	}t[MAX];
    	int tot,last;
    	void init()
    	{
    		t[tot=1].len=-1;
    		t[0].ff=t[1].ff=1;
    	}
    	void extend(int c,int n,char *s)
    	{
    		int p=last;
    		while(s[n-t[p].len-1]!=s[n])p=t[p].ff;
    		if(!t[p].son[c])
    		{
    			int v=++tot,k=t[p].ff;
    			t[v].len=t[p].len+2;
    			while(s[n-t[k].len-1]!=s[n])k=t[k].ff;
    			t[v].ff=t[k].son[c];
    			t[p].son[c]=v;
    			Add(t[v].ff,v);
    		}
    		last=t[p].son[c];
    	}
    	int vis[MAX];
    	void dfs(int u)
    	{
    		if(t[u].len%4==0&&vis[t[u].len/2])ans=max(ans,t[u].len);
    		++vis[t[u].len];
    		for(int i=h[u];i;i=e[i].next)dfs(e[i].v);
    		--vis[t[u].len];
    	}
    }PT;
    int main()
    {
    	PT.init();
    	n=read();
    	scanf("%s",ch+1);
    	Add(1,0);
    	for(int i=1;i<=n;++i)PT.extend(ch[i]-97,i,ch);
    	PT.dfs(1);
    	printf("%d
    ",ans);
    	return 0;
    }
    
    

    upd:
    我更新一下利用(half)来求解的方法
    对于每个节点,我们维护一个(half)来表示长度最长的、不超过它长度一半的那个祖先节点
    这样子只需要判断一下当前点的(half)长度是否是一半,并且当前串的长度是四的倍数就好了

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    using namespace std;
    #define ll long long
    #define RG register
    #define MAX 555555
    inline int read()
    {
        RG int x=0,t=1;RG char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        if(ch=='-')t=-1,ch=getchar();
        while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
        return x*t;
    }
    struct Node
    {
    	int son[26],ff;
    	int len;
    }t[MAX];
    int half[MAX];
    int tot,last;
    void init(){t[tot=1].len=-1;t[0].ff=t[1].ff=1;}
    void extend(int c,int n,char *s)
    {
    	int p=last;
    	while(s[n-t[p].len-1]!=s[n])p=t[p].ff;
    	if(!t[p].son[c])
    	{
    		int v=++tot,k=t[p].ff;
    		t[v].len=t[p].len+2;
    		while(s[n-t[k].len-1]!=s[n])k=t[k].ff;
    		t[v].ff=t[k].son[c];
    		t[p].son[c]=v;
    		if(t[v].len==1)half[v]=0;
    		else
    		{
    			int pos=half[p];
    			while(s[n-t[pos].len-1]!=s[n]||(t[pos].len+2)*2>t[v].len)
    				pos=t[pos].ff;
    			half[v]=t[pos].son[c];
    		}
    	}
    	last=t[p].son[c];
    }
    int n,ans;
    char ch[MAX];
    int main()
    {
    	scanf("%d%s",&n,ch+1);init();
    	for(int i=1;i<=n;++i)extend(ch[i]-97,i,ch);
    	for(int i=1;i<=tot;++i)
    		if(t[half[i]].len*2==t[i].len&&t[i].len%4==0)
    			ans=max(ans,t[i].len);
    	printf("%d
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    链接<a href="javascript:void(0)" />
    JS 不定函数参数argument的用法
    java编译期常量
    对于代码设计的思考
    java交集retainAll 和 Sets.intersection 性能比较
    java 递归调整为队列
    前端跨域问题springboot
    guava的map中value转换问题和排序问题
    MAC安装 Office2016
    股市中的资金流入流出
  • 原文地址:https://www.cnblogs.com/cjyyb/p/8460254.html
Copyright © 2011-2022 走看看