luogu P3311 [SDOI2014]数数
题目大意
题目中已经说得比较清楚了吧
就是求小于等于n的数字中,不包含给出一些字串的数的个数
m
o
d
1
0
9
+
7
mod \ \ \ \ 10^9+7
mod 109+7
就这样
嗯
题解
也是先按照套路建AC自动机,然后数位DP
要开4维
分别表示
第几位 节点 limit 是否匹配到
因为有前导0,所以要多开一维
所以是
第几位 节点 limit 是否匹配到 zero
然后就可以愉快地开始DP了
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define mod 1000000007
#define N 1505
#define int long long
#define C 10
using namespace std;
int ch[N][C], nxt[N * C], vis[N], a[N], tot, L, R, K, n;
unsigned int dp[1203][N][2][2][2];
string st;
void insert(){
int len = st.length(), p = 0;
for(int i = 0; i < len; i ++){
if(!ch[p][st[i] - '0']) ch[p][st[i] - '0'] = ++ tot;
p = ch[p][st[i] - '0'];
}
vis[p] = 1;
}
queue<int> q;
void build(){
int p = 0;
for(int i = 0; i < 10; i ++) if(ch[p][i]) q.push(ch[p][i]);
while(q.size()){
int u = q.front(); q.pop();
for(int i = 0; i < 10; i ++){
if(ch[u][i]){
nxt[ch[u][i]] = ch[nxt[u]][i];
vis[ch[u][i]] |= vis[ch[nxt[u]][i]];
q.push(ch[u][i]);
} else ch[u][i] = ch[nxt[u]][i];
}
}
}
void solve(){
memset(dp, 0, sizeof dp);
cin >> st;
int ws = st.length();
for(int i = 1; i <= ws; i ++) a[i] = st[i - 1] - '0';
cin >> n;
for(int i = 1; i <= n; i ++){
cin >> st;
insert();
}
build();
dp[0][0][1][0][1] = 1; // 第几位 节点 limit 是否匹配到 zero
for(int i = 0; i < ws; i ++){//第几位
for(int j = 0; j <= tot; j ++){//AC自动机上的节点
for(int limit = 0; limit <= 1; limit ++){//是否到上界
for(int zero = 0; zero <= 1; zero ++){//是否有前导0
for(int pp = 0; pp <= 1; pp ++){//是否匹配到
if(!dp[i][j][limit][pp][zero]) continue;//蜜汁剪枝
for(int k = 0; k < 10; k ++){//枚举i+1位是什么数字来转移
int v = ch[j][k];
if(zero) v = ch[0][k];
if(limit && k == a[i + 1]){//看有没有到上界
dp[i + 1][v][1][vis[v] | pp][zero && (!k)] += dp[i][j][limit][pp][zero], dp[i + 1][v][1][vis[v] | pp][zero && (!k)] %= mod;//转移
break;//到上界就break
}else dp[i + 1][v][0][vis[v] | pp][zero && (!k)] += dp[i][j][limit][pp][zero], dp[i + 1][v][0][vis[v] | pp][zero && (!k)] %= mod;//正常的转移
}
}
}
}
}
}
long long ret = 0;
for(int i = 0; i <= tot; i ++) ret += dp[ws][i][0][0][1] + dp[ws][i][1][0][1] + dp[ws][i][0][0][0] + dp[ws][i][1][0][0], ret %= mod;//累加答案
cout << (ret - 1 + mod) % mod;//记得减一,不算0
}
signed main(){
solve();
return 0;
}
坑点
前导零……