【回文树 && 求本质不同回文串的和】ACM-ICPC 2018 南京赛区网络预赛 I. Skr

Step1 Problem:

给你一个字符串 s,求本质不同的回文字符串的加和 mod 1e9+7。
例:s = “1111”, ans = 1111 + 111 + 11 + 1.
数据范围:
1 <= len <= 2e6. 1 <= s[i] <= 9.

Step2 Ideas:

前置技能:4348 = ((4*10 + 3)*10+4)*10 + 8,这样我们就可以随便取 mod 了。
分别从 0,1 开始 bfs. 偶数奇数分开算
0 偶数:
当前回文串为空 其结果为 ans = 0,下次变成 11 = 1*10 + ans*10 + 1.
当前回文串为 11 其结果为 ans = 11,下次变成 1111 = 1*1000 + ans*10 + 1.
1 奇数:
当前回文串为空 其结果为 ans = 0,下次变成 5 = 5.//预处理,即可
当前回文串为 5 其结果为 ans = 5,下次变成 555 = 1*100 + ans*10 + 5.
当前回文串为 555 其结果为 ans = 555+5 = 560,下次变成 55555 = 1*10000 + ans*10 + 5.

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e6+5;
const int MOD = 1e9+7;
struct node
{
    int len, cnt, pos;
    int next[10], fail;//只有26个小写字母
}a[N];
int top, last;
char s[N];
void Init_Tr()
{
    top = 1, last = 0;
    a[0].len = 0, a[1].len = -1;
    a[0].fail = 1;
}
int i;//减少传参可以优化很大的时间复杂度
int get_id(int now)
{
    while(s[i] != s[i-a[now].len-1]) now = a[now].fail;
    return now;
}
void Insert()
{
    int len = strlen(s+1);
    for(i = 1; i <= len; i++) {
        int t = s[i]-'0';
        int id = get_id(last);
        if(!a[id].next[t]) {
            a[++top].len = a[id].len + 2;
            a[top].pos = i;
            a[top].fail = a[get_id(a[id].fail)].next[t];
            a[id].next[t] = top;
        }
        last = a[id].next[t];
        a[last].cnt++;
    }
}
struct Q
{
    int pos;
    ll now, z;
};
ll solve()
{
    ll ans = 0;
    queue<Q> q;
    for(int i = 0; i < 10; i++) {//奇数预处理长度为 1 的回文串
        if(a[1].next[i]) {
            q.push((Q){a[1].next[i], i, 100});
        }
    }
    while(!q.empty()) {
        Q u = q.front(); q.pop();
        ans += u.now;
        ans %= MOD;
        for(int i = 0; i < 10; i++) {
            if(a[u.pos].next[i]) {
                ll now = i*u.z + u.now*10 + i;
                now %= MOD;
                ll z = u.z*100; z %= MOD;
                q.push((Q){a[u.pos].next[i], now, z});
            }
        }
    }
    q.push((Q){0, 0, 10});
    while(!q.empty()) {
        Q u = q.front(); q.pop();
        ans += u.now;
        ans %= MOD;
        for(int i = 0; i < 10; i++) {
            if(a[u.pos].next[i]) {
                ll now = i*u.z + u.now*10 + i;
                now %= MOD;
                ll z = u.z*100; z %= MOD;
                q.push((Q){a[u.pos].next[i], now, z});
            }
        }
    }
    return ans;
}
int main()
{
    scanf("%s", s+1);
    //1111111111111
    Init_Tr();
    Insert();
    printf("%lld\n", solve());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值