zoj 3494 BCD Code
这题做的真是太艰难,水平太菜……是我的第一道ac自动机+ 数位dp的题目
思路都很容易想,不过这题要注意的实在太多了,稍不注意就让你调试半天,我就是搞了一个下午
1) 串输入进来的时候是高位在数组下标0,所以要反串
2) 前导0不能忽略考虑
3) A串要减1
4)dp每次case都要初始化为-1,应为每次的ac自动机建立的状态都不一样
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int mod=1000000009; const int NODE=2010; typedef long long ll; ll dp[210][NODE]; char digit[210]; char dig[30]; int chd[NODE][2],word[NODE],fail[NODE],sz; void ins(char *s) { int p=0,len=strlen(s); for(int i=0;i<len;i++) { int id=s[i]-'0'; if(!chd[p][id]){ memset(chd[sz],0,sizeof(chd[sz])); word[sz]=0; chd[p][id]=sz++; } p=chd[p][id]; } word[p]=1; } int Que[NODE]; void ac() { int *s=Que,*e=Que; for(int i=0;i<2;i++) if(chd[0][i]) fail[chd[0][i]]=0,*e++=chd[0][i]; while(s!=e) { int p=*s++; for(int i=0;i<2;i++) if(chd[p][i]) { *e++=chd[p][i]; fail[chd[p][i]]=chd[fail[p]][i]; word[chd[p][i]]|=word[fail[chd[p][i]]]; }else chd[p][i]=chd[fail[p]][i]; } } void init(int &ns,int d) { for(int i=3;i>=0;i--) { int id=(d&(1<<i))?1:0; ns=chd[ns][id]; if(word[ns]) { ns=-1;break; } } } ll dfs(int pos,int state,bool doing,bool ok) { if(pos<0) return 1; if(ok&&!doing&&dp[pos][state]!=-1) return dp[pos][state]; int end=doing?(digit[pos]-'0'):9; ll ans=0; for(int i=0;i<=end;i++) { int ns=state; if(ok||i) init(ns,i); if(ns!=-1) ans+=dfs(pos-1,ns,doing&&i==end,ok||i); ans%=mod; } if(ok&&!doing) dp[pos][state]=ans; return ans; } void sub(char *s) { int len=strlen(s),i; for(i=len-1;i>=0&&s[i]=='0';i--) s[i]='9'; s[i]--; } ll cal(char *s) { int len=strlen(s); int tot=0; for(int i=len-1;i>=0;i--) digit[tot++]=s[i]; return dfs(tot-1,0,1,0); } int main() { int T,n; scanf("%d",&T); while(T--) { scanf("%d",&n); memset(dp,-1,sizeof(dp)); sz=1; memset(chd[0],0,sizeof(chd[0])); for(int i=0;i<n;i++) { scanf("%s",dig); ins(dig); } ac(); char tmp[210]; scanf("%s",tmp); sub(tmp); ll ans1=cal(tmp); scanf("%s",tmp); ll ans2=cal(tmp); printf("%lld\n",((ans2-ans1)%mod+mod)%mod); } return 0; }