【51nod1587】半现串

  • 本文为博主原创,未经许可不得转载

这是一种套路:用AC自动机+数位dp来统计某种必须出现的串或者不能出现的串的数量

首先我们把所有长度为d/2的串建成AC自动机

设$f[i][j][0/1]$表示前i位匹配到了AC自动机上的结点j是否匹配上的方案数

则$f[i][j][k]→f[i+1][tr[j][x]][k|tr[j][x]]$ (x为当前枚举数位上的数)

我用的是记忆化搜索,感觉记忆化搜索根本不用动脑子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include<bits/stdc++.h>
#define FILE "read"
using namespace std;
typedef long long ll;
const int mod=(int)1e9+7;
int n,d,cnt,num[55],vis[50010],fail[50010],tr[50010][10];
ll ans,f[55][50010][2];
char ch[1010],a[55],b[55],s[55];
queue<int>q;
inline int read(){
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
void extend(){
int now=0;
for(int i=1;i<=(d>>1);++i){
if(!tr[now][s[i]-'0']) tr[now][s[i]-'0']=++cnt;
now=tr[now][s[i]-'0'];
}
vis[now]=1;
}
void build(){
for(int i=0;i<10;++i) if(tr[0][i]) q.push(tr[0][i]);
while(!q.empty()){
int now=q.front(); q.pop();
for(int i=0;i<10;++i){
if(!tr[now][i]) tr[now][i]=tr[fail[now]][i];
else fail[tr[now][i]]=tr[fail[now]][i],q.push(tr[now][i]);
}
}
}
ll dfs(int i,int j,int k,int flag){
if(i==d+1) return k;
if(!flag&&f[i][j][k]!=-1) return f[i][j][k];
ll ret=0;
for(int x=0;x<=(flag?num[i]:9);++x)
(ret+=dfs(i+1,tr[j][x],k|vis[tr[j][x]],flag&&(x==num[i])))%=mod;
if(!flag) f[i][j][k]=ret;
return ret;
}
bool check(){
int now=0,ret=0;
for(int i=1;i<=d;++i){
now=tr[now][a[i]-'0'];
ret|=vis[now];
}
return ret;
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
scanf("%s%s%s",ch+1,a+1,b+1);
n=strlen(ch+1); d=strlen(a+1);
for(int i=1;i<=n-(d>>1)+1;++i){
for(int j=i;j<=i+(d>>1)-1;++j)
s[j-i+1]=ch[j];
extend();
}
build();
memset(f,-1,sizeof(f));
for(int i=1;i<=d;++i) num[i]=b[i]-'0';
ans+=dfs(1,0,0,1);
for(int i=1;i<=d;++i) num[i]=a[i]-'0';
ans-=dfs(1,0,0,1);
ans+=check(); ans=(ans%mod+mod)%mod;
printf("%lld\n",ans);
return 0;
}
文章目录
,