CF 835D Palindromic characteristics 题解

做法一:字符串哈希

暴力枚举子串,Hash 判断两边是否可行,时间复杂度 $O(n^2 \log n)$。

做法二:DP

这篇文章 写的很清楚。

做法三:PAM

当数据范围 $1 \leq |s| \leq 5000000$ 时,前两种方法是过不去的,能过去也可以被 Hack。所以我们考虑线性的做法,而 PAM 在插入之后上传 $fail$ 树,使 $cnt$ 数组可以 $O(n)$ 求出。

设 $g_p$ 表示长度小于 $\dfrac{len_p}{2}$ 的最长回文后缀,$f_p$ 表示回文串 $\text{p}$ 的阶数,易得以下结论:

$$
\begin{cases}
 f_p=1 & \text{ if } \dfrac{len_p}{2} \neq len_{g_{p}} \\
 f_p=f_{g_{p}}+1 & \text{ otherwise } 
\end{cases}
$$

统计答案时,枚举 $p$,令 $ans_{f_p}+=cnt_p$,最终答案就是对 $ans$ 数组求出后缀和。

namespace LZX
{
    using namespace std;
    const int MAXN=5000005;
    int s[MAXN],n,ch[MAXN][26],idx,fail[MAXN],last,len[MAXN],cnt[MAXN],g[MAXN],f[MAXN];
    long long ans[MAXN];
    char str[MAXN];
    int s_PAM_newnode(int l)
    {
        len[++idx]=l;
        return idx;
    }
    void s_PAM_clear()
    {
        idx=-1;
        n=last=0;
        s_PAM_newnode(0);
        s_PAM_newnode(-1);
        fail[0]=1;
        s[0]=-1;
        return;
    }
    int s_PAM_getfail(int p)
    {
        while(s[n]^s[n-len[p]-1])
        {
            p=fail[p];
        }
        return p;
    }
    void s_PAM_insert(int awa)
    {
        int p,c,now;
        s[++n]=awa;
        p=s_PAM_getfail(last);
        if(!ch[p][awa])
        {
            c=s_PAM_newnode(len[p]+2);
            fail[c]=ch[s_PAM_getfail(fail[p])][awa];
            if(len[c]<=2)
            {
                g[c]=fail[c];
            }
            else
            {
                now=g[p];
                while(s[n]^s[n-len[now]-1]||(len[now]+2)*2>len[c])
                {
                    now=fail[now];
                }
                g[c]=ch[now][awa];
            }
            ch[p][awa]=c;
        }
        last=ch[p][awa];
        cnt[last]++;
        return;
    }
    int _main()
    {
        int l;
        scanf("%s",str);
        l=strlen(str);
        s_PAM_clear();
        for(int i=0;i<l;i++)
        {
            s_PAM_insert(str[i]-'a');
        }
        for(int i=idx;i>=2;i--)
        {
            cnt[fail[i]]+=cnt[i];
        }
        for(int i=2;i<=idx;i++)
        {
            if(len[i]/2==len[g[i]])
            {
                f[i]=f[g[i]]+1;
            }
            else
            {
                f[i]=1;
            }
            ans[f[i]]+=1ll*cnt[i];
        }
        for(int i=l;i;i--)
        {
            ans[i]+=ans[i+1];
        }
        for(int i=1;i<=l;i++)
        {
            printf("%lld ",ans[i]);
        }
        printf("\n");
        return 0;
    }
}

 

posted @ 2022-04-11 15:12  Day_Dreamer_D  阅读(50)  评论(1)    收藏  举报