题目大意:洛谷
题解:观察到(n,d)都很小,所以我们可以把(s)中所有长度为(left lfloor frac{d}{2} ight floor)的子串全部拿出来建一个 AC 自动机,然后将限制差分一下改成小于等于一个数的有多少个,然后直接跑一遍非常暴力的数位 DP 即可。
更加具体的,设状态(f_{i,j,0/1,0/1})表示当前 DP 到第(i)为,在自动机上的节点编号为(j),是否达到上界,是否出现长度为(left lfloor frac{d}{2} ight floor)的子串的字符串个数,时间复杂度为(O(ncdot d^2cdot S))((S)为字符集大小)。
代码:
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int Maxn=1000;
const int Maxd=50;
const int Mod=1000000007;
struct Node{
int ch[10];
int dep,fail;
}node[Maxn*Maxd+5];
int id_tot;
char s[Maxn+5],x[Maxn+5],y[Maxn+5];
int n,d;
void add(char *s,int len){
int root=0;
for(int i=1;i<=len;i++){
if(node[root].ch[s[i]]==0){
node[root].ch[s[i]]=++id_tot;
node[id_tot].dep=node[root].dep+1;
}
root=node[root].ch[s[i]];
}
}
void init_bfs(){
queue<int> q;
for(int i=0;i<10;i++){
if(node[0].ch[i]){
q.push(node[0].ch[i]);
node[node[0].ch[i]].fail=0;
}
}
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<10;i++){
if(node[u].ch[i]==0){
node[u].ch[i]=node[node[u].fail].ch[i];
}
else{
node[node[u].ch[i]].fail=node[node[u].fail].ch[i];
q.push(node[u].ch[i]);
}
}
}
}
int f[Maxd+5][Maxn*Maxd+5][2][2];
int calc(char *s,int len){
memset(f,0,sizeof f);
f[0][0][1][0]=1;
for(int i=0;i<len;i++){
for(int j=0;j<=id_tot;j++){
for(int a=0;a<=1;a++){
for(int b=0;b<=1;b++){
if(f[i][j][a][b]==0){
continue;
}
for(int k=0;k<10;k++){
if(a==1&&k>s[i+1]){
break;
}
int &tmp=f[i+1][node[j].ch[k]][a&(k==s[i+1])][b|(node[node[j].ch[k]].dep==(len>>1))];
tmp=(tmp+f[i][j][a][b])%Mod;
}
}
}
}
}
int ans=0;
for(int i=0;i<=id_tot;i++){
ans=(ans+f[len][i][1][1])%Mod;
ans=(ans+f[len][i][0][1])%Mod;
}
return ans;
}
int main(){
scanf("%s",s+1);
while(s[++n]!=' ');
n--;
scanf("%s",x+1);
while(x[++d]!=' ');
d--;
scanf("%s",y+1);
for(int i=1;i<=n;i++){
s[i]-='0';
}
for(int i=1;i<=d;i++){
x[i]-='0';
y[i]-='0';
}
for(int i=1;i<=(n-(d>>1)+1);i++){
add(s+i-1,d>>1);
}
init_bfs();
x[d]--;
int tmp=d;
while(x[tmp]<0){
x[tmp-1]--;
x[tmp]=9;
tmp--;
}
printf("%d
",(calc(y,d)-calc(x,d)+Mod)%Mod);
return 0;
}