【CF835D】Palindromic characteristics 加强版
Description
给你一个串,让你求出(k)阶回文子串有多少个。(k)从(1)到(n)。
(k)阶子串的定义是:子串本身是回文串,而且它的左半部分也是回文串。
首先明确:
-
如果一个串是(k)阶回文,那他一定还是(k-1)阶回文。
-
如果一个串是(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
",ans[i]);
return 0;
}
2018.12.14