ACM-ICPC 2018 南京赛区网络预赛 - Skr (回文树)

ACM-ICPC 2018 南京赛区网络预赛 - Skr

题意:

求一个不含0的大整数的所有不同回文字串的和

如 : 1121

有 1121、112、121、11、12、21、1、2 八个不同的子串,其中121、11、1、2为回文串

所以其答案为 121 + 11 + 1 + 2 = 135 

 可以利用回文树得到所有不同的回文串,然后遍历这些回文串,得到所有的回文串的值求和即可。

每个回文串的值有一个递推的关系,可以利用树形DP来得到。

#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
typedef long long int LL;
const int MAXN = 2e6+10;
const int N = 11;
const int mod = 1000000007;
struct Palindromic_Tree {
    int next[MAXN][N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
    int fail[MAXN];//fail指针,失配后跳转到fail指针指向的节点
    int cnt[MAXN];//cnt[i]表示节点i表示的回文串在原串中出现的个数,要用count
    int num[MAXN];//num[i]表示节点i表示的回文串中有几种不同且以该字符结尾的回文串个数
    int len[MAXN];//len[i]表示节点i表示的回文串的长度
    int S[MAXN];//存放添加的字符
    int last;//指向上一个字符所在的节点,方便下一次add
    int n;//字符数组指针
    int p;//节点指针,节点数
    // 0 表示对称轴为空'abba',1 表示对称轴为字符'aba' 起点
    int newnode (int l) {//新建节点
        for (int i=0; i<N; ++i) next[p][i] = 0;
        cnt[p] = 0; num[p] = 0; len[p] = l;
        return p ++;
    }
    void init() {//初始化
        p = 0;
        newnode(0);
        newnode(-1);
        last = 0;
        n = 0;
        S[n] = -1;//开头放一个字符集中没有的字符,减少特判
        fail[0] = 1;
    }
    
    int get_fail(int x) {//和KMP一样,失配后找一个尽量最长的
        while(S[n-len[x]-1]!=S[n]) x = fail[x];
        return x;
    }
    void add(int c) {
        c -= '0'; // 字符为 a ~ z
        S[++ n] = c;
        int cur = get_fail(last);//通过上一个回文串找这个回文串的匹配位置
        if (!next[cur][c]) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
            int now = newnode(len[cur] + 2);//新建节点
            fail[now] = next[get_fail(fail[cur])][c];//和AC自动机一样建立fail指针,以便失配后跳转
            next[cur][c] = now;
            num[now] = num[fail[now]] + 1;
        }
        last = next[cur][c];
        cnt[last] ++;
    }
    
    void count() {
        for(int i=p-1; i>=0; --i) cnt[fail[i]] += cnt[i] ;
        //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
    }
}_tree;

LL ans = 0, _10[MAXN];
char s[MAXN];
void dfs(int x, int op, LL sum) {
    ans = (ans + sum) % mod;
    for(int i=1;i<=9;i++) {
        int nt = _tree.next[x][i];
        if(!nt) continue;
        LL to = (sum * 10 % mod + i + i * _10[_tree.len[nt]-1] % mod) % mod;
        if(op && _tree.len[nt]==1) to /= 2;
        dfs(nt, op, to % mod);
    }
}
int main()
{
    _10[0] = 1;
    scanf("%s", s); int len = strlen(s);
    for(int i=1;i<=len;i++) _10[i] = (_10[i-1] * 10) % mod;
    _tree.init();
    for(int i=0;i<len;i++) _tree.add(s[i]);
    dfs(0, 0, 0); dfs(1, 1, 0);
    printf("%lld\n", ans % mod);
    return 0;
}

回文树学习链接

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值