1. 程式人生 > >ACM-ICPC 2018 南京賽區網路預賽 I. Skr (迴文樹)

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

題目來源

題意:

給了一個數字的字串,問這個字串中連續的子串是迴文串的值的和。

思路:

構建一棵迴文樹,每次新增的時候如果判斷髮現是新出現的字串則答案需要加上這個值,在計算這個值的時候,可以由它的上一個狀態節點轉移過來,特判一下上一個狀態是奇根的就行了。

程式碼:

#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;
}