回文树的应用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38786088/article/details/82413914

回文树的应用

听说,这个算法能将一些难处理的字符串题目变成裸题,菜菜学学!没想到18南京网络赛碰到了,居然想不起来了,可恶!

计蒜客 30998
题目: 问字符串(‘0’~’9’)里面不同本质的数字字符串代表的数字之和模(1e9+7).

回文树裸题,设置en[i]数组,来记录节点代表回文串的末尾在字符串中的id,代表的数字取模我们可以用前缀和来实现: 444123321 中123321的值:

s u m [ 444123321 ] s u m [ 444 ] 10 6 ; 123321 0

#include <bits/stdc++.h>
#define llt long long
using namespace std;

const int maxn = 2e6+77;
const int N = 10;
const int mod = 1e9+7;
struct PT{//回文树/回文自动机
    int last,n,p; // last 记录已an结尾的最长回文串的编号 n 记录插好的字符数 p记录used的节点数
    int S[maxn],nxt[maxn][N],len[maxn],en[maxn],fail[maxn];

    int newnode(int l){
        for(int i=0;i<10;++i) nxt[p][i] = 0;
        len[p] = l;
        return p++;
    }
    void init(){
        last = n = p =0;
        S[0]  = -1;
        fail[0] = 1;
        newnode(0); newnode(-1);
    }
    int find_fail(int x){
        while(S[n]!=S[n-1-len[x]]) x = fail[x];
        return x;
    }
    void add(int a){
        S[++n] = a;
        int cur = find_fail(last);
        if(!nxt[cur][a]){
            int now = newnode(len[cur]+2);
            fail[now] = nxt[find_fail(fail[cur])][a];
            nxt[cur][a] = now;
            en[now] = n;
        }
        last = nxt[cur][a];
    }
}p;

char s[maxn];
int sum[maxn],pow_10[maxn];
int main(){
    scanf("%s",s+1);
    int len = strlen(s+1);
    p.init();
    for(int i=1;i<=len;++i)
        p.add(s[i]-'0');
    sum[0]=0; pow_10[0]=1;
    for(int i=1;i<=len;++i){
        sum[i] = (1ll*sum[i-1]*10%mod+s[i]-'0')%mod;
        pow_10[i] = 10ll*pow_10[i-1]%mod;
    }
    llt ans = 0;
    for(int i=2;i<p.p;++i){
        ans = (ans + sum[p.en[i]])%mod;
        ans = (ans - 1ll*sum[p.en[i]-p.len[i]]*pow_10[p.len[i]]%mod+mod)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38786088/article/details/82413914