2019牛客多校第六场 Palindrome Mouse

题意:求每个本质不同的回文串的所有回文子串的总数

思路:我们统计一个串的回文子串的个数,可以将这些子串分为两类,一类是父亲节点的所有子串(包含父亲节点),另一种是所有可能的回文后缀。注意着两者有交集,所以需要标记是否访问过。这两种包含所有的情况,原因是一个字符串的所有回文子串可以分为两类,一类是包含最后一个字符的,另一类是不包含最后一个字符的。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 300005;
char str[maxn];
struct PAM{
    int last,         //当前最长回文后缀的节点标号
    n,                //字符串的长度
    SZ;               //节点的个数
    int ch[maxn][26], // 儿子指针
    fail[maxn],      // 指向长度小于当前节点的最长回文后缀
    cnt[maxn],       //节点所代表的回文串作为最长回文后缀出现的次数 或 长度小于该串的回文子串的个数
    len[maxn],       //节点所代表的回文后缀的长度
    s[maxn],         //字符串
    vis[maxn];
    void init(int _n) {
        SZ=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[++SZ]=ch[getfail(fail[cur])][t];
            ch[cur][t]=SZ,len[SZ]=len[cur]+2;
        }
        last=ch[cur][t];
    }
    void dfs( int x,int fa ){
        if( fa >= 2 ){
            cnt[x] += cnt[fa];
            cnt[x]++;
        }
        vis[fa] = 1;
        stack<int> st;
        int p = fail[x];
        while( p >1 && !vis[p] ){
            cnt[x]++;
            vis[p] = 1;
            st.push( p );
            p = fail[p];
        }
        for( int i = 0;i < 26;i++ ){
            if( !ch[x][i] ) continue;
            dfs(ch[x][i],x);
        }
        vis[fa] = 0;
        while( st.size() ){
            vis[ st.top() ] = 0;
            st.pop();
        }
    }
    LL count() {
        LL re = 0;
        dfs( 0,0 );
        dfs(1,0);
        for( int i = 2;i <= SZ;i++ ){
            re += cnt[i];
        }
        return re;
    }
}g;
int main(){
    int T;
    scanf("%d",&T);
    for(int tt = 1;tt <= T;tt++) {
        printf("Case #%d: ",tt);
        scanf("%s", str);
        int len = strlen(str);
        g.init(len);
        for (int i = 0; i < len; i++) {
            g.add(str[i] - 'a');
        }
        cout << g.count() << endl;
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值