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

A number is skr, if and only if it's unchanged after being reversed. For example, "12321", "11" and "1" are skr numbers, but "123", "221" are not. FYW has a string of numbers, each substring can present a number, he wants to know the sum of distinct skr number in the string. FYW are not good at math, so he asks you for help.

Input

The only line contains the string of numbers SS.

It is guaranteed that 1 \le S[i] \le 91≤S[i]≤9, the length of SS is less than 20000002000000.

Output

Print the answer modulo 10000000071000000007.

样例输入1复制

111111

样例输出1复制

123456

样例输入2复制

1121

样例输出2复制

135

题目来源

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

题意:

给了一个数字的字符串,问这个字符串中连续的子串是回文串的值的和。

思路:

构建一棵回文树,每次添加的时候如果判断发现是新出现的字符串则答案需要加上这个值,在计算这个值的时候,可以由它的上一个状态节点转移过来,特判一下上一个状态是奇根的就行了。

代码:

#include <bits/stdc++.h>

using namespace std;
const int maxn=2e6+5;
const int mod=1e9+7;
typedef long long ll;
char str[maxn];
ll ans;
ll quickmod(int a,int b)
{
    ll ans=1;
    ll base=a;
    while(b)
    {
        if(b&1)
            ans=(ans*base)%mod;
        base=(base*base)%mod;
        b>>=1;
    }
    return ans;
}
struct Palindromic_Tree
{
    int next[maxn][26] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
    int fail[maxn] ;//fail指针,失配后跳转到fail指针指向的节点
    int cnt[maxn] ; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)
    ll num[maxn] ; //表示存放的回文串的值
    int len[maxn] ;//len[i]表示节点i表示的回文串的长度(一个节点表示一个回文串)
    int S[maxn] ;//存放添加的字符
    int last ;//指向新添加一个字母后所形成的最长回文串表示的节点。
    int n ;//表示添加的字符个数。
    int p ;//表示添加的节点个数。

    int newnode ( int l )  //新建节点
    {
        for ( int i = 0 ; i < 26 ; 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' ;
        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 ;
            if(len[cur]==-1)
            {
                ans=(ans+c)%mod;
                num[now]=c;
            }
            else
            {
                num[now]=((num[cur]*10)%mod+c)%mod+((c*quickmod(10,len[now]-1))%mod)%mod;
                ans=(ans+num[now])%mod;
            }
        }
        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;
int main()
{
    scanf("%s",str);
    int len=strlen(str);
    tree.init();
    for(int i=0;i<len;i++)
    {
        tree.add(str[i]);
    }
    printf("%lld\n",ans);
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值