【CF835D】Palindromic characteristics 加强版 解题报告

【CF835D】Palindromic characteristics 加强版

Description

给你一个串,让你求出\(k\)阶回文子串有多少个。\(k\)\(1\)\(n\)

\(k\)阶子串的定义是:子串本身是回文串,而且它的左半部分也是回文串。

首先明确:

  1. 如果一个串是\(k\)阶回文,那他一定还是\(k-1\)阶回文。

  2. 如果一个串是\(k\)阶回文,那么这个串需要满足:

  • 它本是是回文的。
  • 他的左半部分是\(k-1\)回文的。

Input

一个字符串\(s\)

Output

\(n\)行。第\(i\)行输出\(s\)有多少个子串是\(i−\)回文串。

HINT

对于\(100\%\)的数据:\(1≤n≤5000000\)$仅包含小写字母


思路:建出\(PAM\)之后维护一个\(f_i\)代表长度小于一半的最长回文后缀,不要暴力去更新\(Ta\),也不用调跳倍增,直接借助父亲节点的这个数组缩小一半的范围就是\(O(n)\)的了。


Code:

#include <cstdio>
#include <cstring>
#define ll long long
const int N=5e3+10;
char s[N];
int ch[N][26],len[N],fail[N],kth[N],f[N],n,tot;
ll ans[N],siz[N];
int getfail(int now,int p)
{
    while(s[p]!=s[p-len[now]-1]) now=fail[now];
    return now;
}
void PAM()
{
    len[0]=0,len[++tot]=-1,f[0]=fail[0]=1;
    scanf("%s",s+1);n=strlen(s+1);
    for(int las=0,i=1;i<=n;i++)
    {
        int cur=getfail(las,i),c=s[i]-'a';
        if(!ch[cur][c])
        {
            int now=++tot;
            fail[now]=ch[getfail(fail[cur],i)][c];
            ch[cur][c]=now;
            len[now]=len[cur]+2;
            if(len[fail[now]]<=len[now]>>1) f[now]=fail[now];
            else
            {
                int p=f[cur];
                while((len[p]+2>len[now]>>1)||(s[i]!=s[i-len[p]-1])) p=fail[p];
                f[now]=ch[p][c];
            }
        }
        ++siz[las=ch[cur][c]];
    }
    for(int i=tot;i;i--) siz[fail[i]]+=siz[i];
    for(int i=2;i<=tot;i++)
    {
        if(len[f[i]]==len[i]>>1)
            kth[i]=kth[f[i]]+1;
        else
            kth[i]=1;
        ans[kth[i]]+=siz[i];
    }
}
int main()
{
    PAM();
    for(int i=n;i;i--) ans[i]+=ans[i+1];
    for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
    return 0;
}

2018.12.14

posted @ 2018-12-14 19:04  露迭月  阅读(311)  评论(0编辑  收藏  举报