【XSY2534】【CF835D】Palindromic characteristics 回文自动机

题目大意

​  一个字符串\(s\)\(1\)−回文串当且仅当这个串是回文串。

​  一个串\(s\)\(k\)−回文串\((k>1)\)当且仅当\(s\)的前一半与后一半相同且\(s\)的前一半是非空\((k−1)\)−回文串。

​  一个串\(s\)的前一半是这个串的长度为\(\lfloor\frac{|s|}{2}\rfloor\)的前缀,一个串\(s\)的后一半是这个串的长度为\(\lfloor\frac{|s|}{2}\rfloor\)的后缀

​  有一个字符串\(s\),对于每个\(k\in\{1\ldots n\}\),求出\(s\)的多少个子串是\(k\)−回文串。

​  原题:\(1\leq n\leq 5000\)

​  加强版:\(1\leq n\leq 1000000\)

题解

​  我们可以发现,一个串是\(k\)-回文串,那么这个串就是\((k-1)\)-回文串\((k>1)\)

​  所以可以DP:设\(f_{i,j}\)表示\(s_{i\ldots j}\)最高是几阶回文串

​  时间复杂度:\(O(n^2)\)

​  我们还可以用BZOJ4044那道题的方法,在回文自动机上处理出每个串的长度不超过这个串长一半的最长回文后缀,然后直接转移即可

​  时间复杂度:\(O(n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
int nxt[1000010][30];
int fail[1000010];
int len[1000010];
int trans[1000010];
int a[1000010];
int f[1000010];
int cnt[1000010];
ll ans[1000010];
int p;
int n;
int last;
void init(int x)
{
    int i;
    for(i=1;i<=26;i++)
        nxt[x][i]=2;
}
void add(int x)
{
    a[++n]=x;
    while(a[n-len[last]-1]!=a[n])
        last=fail[last];
    int cur=last;
    if(nxt[cur][x]==2)
    {
        int now=++p;
        init(p);
        len[now]=len[cur]+2;
        last=fail[last];
        while(a[n-len[last]-1]!=a[n])
            last=fail[last];
        fail[now]=nxt[last][x];
        if(len[fail[now]]<=len[now]/2)//没有这部分也可以AC 
            trans[now]=fail[now];
        else
        {
            last=trans[cur];
            while(len[last]+2>len[now]/2||a[n-len[last]-1]!=a[n])
                last=fail[last];
            trans[now]=nxt[last][x];
        }
        nxt[cur][x]=now;
    }
    last=nxt[cur][x];
    cnt[last]++;
}
void solve()
{
    f[1]=0;
    f[2]=0;
    int i;
    for(i=3;i<=p;i++)
        if(len[trans[i]]==len[i]/2)
            f[i]=f[trans[i]]+1;
        else
            f[i]=1;
    for(i=p;i>=3;i--)
        cnt[fail[i]]+=cnt[i];
    for(i=3;i<=p;i++)
        ans[f[i]]+=cnt[i];
}
char s[1000010];
int main()
{
//  freopen("necklace.in","r",stdin);
//  freopen("necklace.out","w",stdout);
    p=0;
    n=0;
    init(++p);
    fail[p]=2;
    len[p]=-1;
    trans[p]=2;
    init(++p);
    fail[p]=1;
    len[p]=0;
    a[0]=-1;
    trans[p]=1;
    last=p;
    int m;
    scanf("%s",s+1);
    m=strlen(s+1);
    int i;
    for(i=1;i<=m;i++)
        add(s[i]-'a'+1);
    solve();
    for(i=m-1;i>=1;i--)
        ans[i]+=ans[i+1];
    for(i=1;i<=m;i++)
        printf("%lld\n",ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/ywwyww/p/8510044.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值