回文树的应用
听说,这个算法能将一些难处理的字符串题目变成裸题,菜菜学学!没想到18南京网络赛碰到了,居然想不起来了,可恶!
计蒜客 30998
题目: 问字符串(‘0’~’9’)里面不同本质的数字字符串代表的数字之和模(1e9+7).
回文树裸题,设置en[i]数组,来记录节点代表回文串的末尾在字符串中的id,代表的数字取模我们可以用前缀和来实现: 444123321 中123321的值:
sum[444123321]−sum[444]∗106;即将123321全变为0
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;
}