I Love Palindrome String【双倍回文】

题目链接:https://vjudge.net/problem/HDU-6599
双倍回文,即要回文串的一半也是一个回文串(对于“一半”的定义具体要看题目)

思路:因为回文树中的fail指针可以遍历到当前结点的所有后缀回文串,所以很容易想到,我们可以直接遍历每个回文串的fail,看它的后缀回文串中是否存在一个长度刚好是它的一半的。但是每次遍历fail要花费一定的时间,所以类似AC自动机的last跳板,我们可以用一个trans数组记录每个回文串的长度≤它的一半的后缀回文串,然后每次找长度为当前回文串一半的后缀回文串时,我们就可以直接从父亲的trans处开始遍历,而不是每次都从父亲的fail开始遍历。因为大于父亲长度一半的结点得到的结果肯定也会大于当前节点所表示的回文串长度的一半。实现过程就是代码if(!net[cur][c])中的if else部分

代码:

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
//#define int long long
const int manx=3e5+10;
const int max_len=2e6+10;
char s[manx];
int ans[manx];
struct pal_tree
{
    int net[manx][26],cnt[manx],S[manx],len[manx],fail[manx],trans[manx];
    int p,last,n;
    int newnode(int l)
    {
        for(int i=0;i<26;i++)
            net[p][i]=0;
        cnt[p]=0;
        len[p]=l;
        return p++;
    }
    void init()
    {
        p=n=0;
        newnode(0);
        newnode(-1);
        last=0;
        fail[0]=1;
        S[0]=-1;
    }
    int getfail(int x)
    {
        while(S[n-len[x]-1]!=S[n])x=fail[x];
        return x;
    }
    void add(int c)
    {
        c-='a';
        S[++n]=c;
        int cur=getfail(last);
        if(!net[cur][c])
        {
            int now=newnode(len[cur]+2);
            fail[now]=net[getfail(fail[cur])][c];
            net[cur][c]=now;
            //num[now]=num[fail[now]]+1;
            if(len[now]<=2)trans[now]=fail[now];
            else
            {
                int temp=trans[cur];
                while(S[n-len[temp]-1]!=S[n]||((len[temp]+2)>(len[now]+1)/2))temp=fail[temp];
                trans[now]=net[temp][c];
            }
        }
        last=net[cur][c];
        cnt[last]++;
    }
    void ccount()
    {
        for(int i=p-1;i>=1;i--)
        {
            cnt[fail[i]]+=cnt[i];
            if(len[i]<=2||(len[i]+1)/2==len[trans[i]])
                ans[len[i]]+=cnt[i];
        }
    }
}tree;
int main()
{
    while(scanf("%s",s)!=EOF)
    {
        memset(ans,0,sizeof(ans));
        int l=strlen(s);
        tree.init();
        for(int i=0;i<l;i++)tree.add(s[i]);
        tree.ccount();
        for(int i=1;i<=l;i++)
            printf("%d%c",ans[i],i==l?'\n':' ');
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值