bzoj 3530: [Sdoi2014]数数
Description
我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串。例如当S=(22,333,0233)时,233是幸运数,2333、20233、3223不是幸运数。给定N和S,计算不大于N的幸运数个数。
解题报告
简单题,但有点坑,首先如果没有限制就是一个数位DP板子,对于包含的限制,一般是加一维在AC自动机上的节点号,这样就可以保证不存在包含关系了.
设状态 \(f[i][j][k][l]\) 表示前i位数字,目前在AC自动机上的j号节点,选的数字为k,是否严格小于\(0/1\),其实我是个傻逼,写完以后猛地发现 \(k\) 这一维是多余的,而且还需要滚动,但还是讲一下我的做法吧,如果严格小于就可以任意选,前缀都相等,就只能选小于 \(N\) 的这一位的数字,如果走出了包含关系,就不转移.
最坑的地方来了:
10
1
03
这一组数据,如果不判前导0,那么输出\(9\), \(3\) 就被完美的忽略了,所以把 \(k\) 这一位加一个状态,表示前导0然后特殊转移即可
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=20005,mod=1e9+7;
int f[2][N][11][2],fail[N],s[N],n,m;char S[N];
struct node{
int nxt[11],mark;
}a[N];
void upd(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int root=0,cnt=0;
void add(){
scanf("%s",S);
int len=strlen(S),p=0;
for(int i=0;i<len;i++){
if(a[p].nxt[S[i]-'0'])p=a[p].nxt[S[i]-'0'];
else a[p].nxt[S[i]-'0']=++cnt,p=cnt;
}
a[p].mark=1;
}
queue<int>q;
void getfail(){
q.push(root);
int x,u,v;
while(!q.empty()){
x=q.front();q.pop();
for(int i=0;i<10;i++){
if(!a[x].nxt[i]){
a[x].nxt[i]=a[fail[x]].nxt[i];
continue;
}
u=fail[x];
while(u && !a[u].nxt[i])u=fail[u];
if(a[u].nxt[i] && a[u].nxt[i]!=a[x].nxt[i])
fail[a[x].nxt[i]]=a[u].nxt[i];
v=a[x].nxt[i];a[v].mark|=a[fail[v]].mark;q.push(v);
}
}
}
void work()
{
scanf("%s%d",S+1,&m);n=strlen(S+1);
for(int i=1;i<=n;i++)s[i]=S[i]-'0';
for(int i=1;i<=m;i++)add();
getfail();
int x,to,b;bool t=0,tt=1;
for(int i=1;i<=s[1];i++){
x=a[root].nxt[i];
if(a[x].mark)continue;
f[t][x][i][i<s[1]]=1;
}
f[t][0][10][1]=1;
for(int i=1;i<n;i++){
for(int j=0;j<=cnt;j++){
for(int k=0;k<=9;k++){
x=f[t][j][k][0];f[t][j][k][0]=0;
if(x){
for(int l=0;l<=s[i+1];l++){
to=a[j].nxt[l];if(a[to].mark)continue;
b=(l<s[i+1]);
upd(f[tt][to][l][b],x);
}
}
x=f[t][j][k][1];f[t][j][k][1]=0;
if(x){
for(int l=0;l<10;l++){
to=a[j].nxt[l];if(a[to].mark)continue;
upd(f[tt][to][l][1],x);
}
}
}
x=f[t][j][10][1];f[t][j][10][1]=0;
for(int l=1;l<=10;l++){
to=a[j].nxt[l];if(a[to].mark)continue;
upd(f[tt][to][l][1],x);
}
}
t^=1;tt^=1;
}
int ans=0;
for(int i=0;i<=cnt;i++)
for(int k=0;k<=10;k++)
for(int l=0;l<=1;l++)
upd(ans,f[t][i][k][l]);
ans=(ans-1+mod)%mod;
printf("%d\n",ans);
}
int main(){work();return 0;}