题意:
给出n个非法串,给出一个范围,求这个范围的数转成定义串不包含非法的个数。所谓定义串,举个例子比如27,7的二进制是0111,2的二进制是0010 那么27变成定义串就是 0010 0111。
题解:
明显是数位dp,ac自动机上的数位dp。一开始把数字全弄成二进制来做,也是脑残了。直接数位dp,dfs就好了,状态很简单dp[pos][i]位数pos,在自动机上i点。注意高进度的减法,范围的下限要减1。还有就是前导零的问题,dfs时多设一个变量判断前导零,这些都是很基本的数位dp,不难。
以前学数位dp就养成了dfs写的习惯,说实话dfs的确好写,感觉无脑操作。递推很容漏掉情况所以不怎么好写。尤其是这类有范围的,特别容易漏。不过在一些情况下递推写法很精简。
#include<iostream> #include<math.h> #include<stdio.h> #include<algorithm> #include<string.h> #include<vector> #include<queue> #include<map> #include<set> using namespace std; #define B(x) (1<<(x)) typedef long long ll; const int oo=0x3f3f3f3f; const ll OO=1LL<<61; const ll MOD=1000000009; const int maxn=208; const int digs=208; const int SIZE=2005; const int alph=2; char A[maxn],B[maxn],str[30]; ll dp[digs][SIZE]; int bit[digs],len; struct AC { int next[SIZE][alph],fail[SIZE],flag[SIZE]; int root,cnt; void Init() { cnt=0; root=newNode(); } int newNode() { for(int i=0;i<alph;i++) next[cnt][i]=-1; flag[cnt++]=0; return cnt-1; } void Insert(char buff[]) { int now=root; int len=strlen(buff); for(int i=0,k;i<len;i++) { k=buff[i]-'0'; if(next[now][k]==-1) next[now][k]=newNode(); now=next[now][k]; } flag[now]=1; } void build() { queue<int>Q; fail[root]=root; int now=root; for(int i=0;i<alph;i++) { if(next[now][i]==-1) next[now][i]=root; else { fail[next[now][i]]=root; Q.push(next[now][i]); } } while(!Q.empty()) { now=Q.front(); Q.pop(); if(flag[fail[now]]==1) flag[now]=1; for(int i=0;i<alph;i++) { if(next[now][i]==-1) next[now][i]=next[fail[now]][i]; else { fail[next[now][i]]=next[fail[now]][i]; Q.push(next[now][i]); } } } } int GetNext(int now,int num) { int temp[4]; for(int i=0;i<4;i++) { temp[i]=num%2; num/=2; } for(int i=3;i>=0;i--) { now=next[now][temp[i]]; if(flag[now]) return -1; } return now; } ll dfs(int pos,int i,int f,int pre0) { if(pos<1) return 1; if(pre0&&!f&&dp[pos][i]!=-1) return dp[pos][i]; int last= f ? bit[pos] : 9; ll res=0; for(int num=0;num<=last;num++) { if(pre0==0&&num==0) { res=(res+dfs(pos-1,0,f&&num==last,pre0)+MOD)%MOD; continue; } int k=GetNext(i,num);///判断是否有包含,没有返回next,有返回-1 if(k!=-1) res=(res+dfs(pos-1,k,f&&num==last,pre0||num)+MOD)%MOD; } if(!f&&pre0==1) dp[pos][i]=res; return res; } ll Cnt(char s[]) { len=strlen(s); for(int i=0;i<len;i++) bit[i+1]=s[len-i-1]-'0'; while(bit[len]==0&&len>0)--len; if(len<1)bit[++len]=0; return dfs(len,0,1,0); } ll DP(char s1[],char s2[]) { memset(dp,-1,sizeof dp); ///将s1减去1 int l1=strlen(s1)-1; for(int i=l1;i>=0;i--) { if(s1[i]>'0') { s1[i]--; break; } else s1[i]='9'; } return (Cnt(s2)-Cnt(s1)+MOD)%MOD; } }; AC ac; int main() { int n,T; scanf("%d",&T); while(T--) { scanf("%d",&n); ac.Init(); for(int i=1;i<=n;i++) { scanf("%s",str); ac.Insert(str); } ac.build(); scanf("%s %s",A,B); cout<<ac.DP(A,B)<<endl; } return 0; } /** 3 1 00 1 10 1 00 1 100 1 1111 1 100 */