2019 杭电多校第二场 string

题意:找到所有的回文串满足它的前一半也是回文串。

思路:回文自动机 + 树上倍增

          我们要处理两件事情,第一件是每种回文串出现了多少次。我们回想回文自动机的构造过程,发现fail指针只会由标号大的指向标号小的。这样我们只需要标号从大到小遍历即可处理出每种回文串出现的次数。

         第二件是倍增的写法,倍增是不需要初始化的因为我们只需要保证 mn[ x ][ 0 ]是对的,后面递推出来的一定是对的。还有根的mn[ root ][ 1 ] ...... mn[ root ][ 20 ]也得是对的。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 300005;
char str[maxn];
LL ans[maxn];
struct PAM{
    int last,n,tot;
    int ch[maxn][26],fail[maxn],len[maxn],vis[maxn],s[maxn],fa[maxn][21],mn[maxn][21];
    LL cnt[maxn];
    void init( int _n ){
        tot = 1,len[1] = -1,s[0] = -1,fail[0] = 1;last = 1;n = 0;
        for( int i = 0;i <= _n+1;i++ ){
            memset( ch[i],0,sizeof(ch[i]) );
            cnt[i] = 0;
        }
    }
    int getfail(int x){
        while( s[n-len[x]-1 ]!=s[n] )x=fail[x];
        return x;
    }
    void add(int t){
        s[++n] = t;int cur = getfail(last);
        if(!ch[cur][t]){
            fail[++tot]=ch[getfail(fail[cur])][t];
            ch[cur][t]=tot,len[tot]=len[cur]+2;
        }
        last=ch[cur][t];
        cnt[last]++;
    }
    void count(){
        for( int i = 2;i <= tot;i++ ){
            fa[i][0] = fail[i];
            for( int j= 1;j <= 20;j++ ){
                fa[i][j] = fa[ fa[i][j-1] ][j-1];
            }
        }
        for( int i = 2;i <= tot;i++ ){
            mn[i][0] = len[ fa[i][0] ];
            for( int j = 1;j <= 20;j++ ){
                mn[i][j] = min( mn[i][j-1],mn[fa[i][j-1]][j-1] );
            }
        }
        for( int i = tot;i >= 2;i-- ){
            cnt[ fail[i] ] += cnt[i];
        }
        int res = 0;
        for( int i = 2;i <= tot;i++ ){
            int p = i;
            for( int t = 20;t >= 0;t-- ){
                if( mn[p][t] >= (len[i]+1)/2 ){
                    p = fa[p][t];
                }
            }
            if( len[p] == (len[i]+1)/2 ){
                ans[len[i]] += cnt[i];
            }
        }
    }
}g;
int main(){
    while( EOF != scanf("%s",str) ){
        int len = strlen(str);
        g.init(len);
        memset( ans,0,sizeof(long long)*(len+1) );
        for( int i = len-1;i >= 0;i-- ){
            g.add(str[i]-'a' );
        }
        g.count();
        printf("%lld",ans[1]);
        for( int i = 2;i <= len;i++ ){
            printf(" %lld",ans[i]);
        }
        puts("");
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值