BZOJ2471 : Count
考虑KMP,设$f[i][j][S]$表示还剩最低$i$位没有确定,目前KMP匹配到了$j$这个位置,前缀匹配情况是$S$,最终会匹配到哪里,中途匹配成功几次。
其中$S[i]$是一个pair<int,long long>,表示对于前面已经确定的高位,如果从$i$开始匹配,那么最终会匹配到哪里,中途匹配成功几次。
对于$f[i][j][S]$的计算,可以枚举最高的还没确定的那一位,重新计算$S$,并将子状态的结果合并。
用记忆化搜索实现这个过程,有效状态数不会很多。
#include<cstdio> #include<cstring> #include<string> #include<map> using namespace std; typedef long long ll; struct P{ int x;ll y; P(){} P(int _x,ll _y){x=_x,y=_y;} P operator+(const P&b){return P(b.x,y+b.y);} }g[10][9],ans; map<string,P>f[20][9]; int n,m,i,j,k,nxt[9];char a[9]; P dfs(int n,int x,string s){ if(!n)return P(s[x]&7,s[x]>>3); if(f[n][x].find(s)!=f[n][x].end())return f[n][x][s]; P v(x,0); for(int i=0;i<10;i++){ string o=s; for(int j=0;j<m;j++){ P t(s[j]&7,s[j]>>3); t=t+g[i][t.x]; o[j]=t.x|(t.y<<3); } v=v+dfs(n-1,v.x,o); } return f[n][x][s]=v; } int main(){ while(~scanf("%d%s",&n,a+1)){ if(!n)return 0; m=strlen(a+1); for(i=1;i<=m;i++)a[i]-='0'; for(nxt[1]=j=0,i=2;i<=m;nxt[i++]=j){ while(j&&a[j+1]!=a[i])j=nxt[j]; if(a[j+1]==a[i])j++; } for(i=0;i<m;i++)for(j=0;j<10;j++){ for(k=i;k&&a[k+1]!=j;k=nxt[k]); if(a[k+1]==j)k++; if(k<m)g[j][i]=P(k,0);else g[j][i]=P(nxt[k],1); } for(i=0;i<n;i++)for(j=0;j<m;j++)f[i][j].clear(); ans=P(0,0); for(i=0;i<n;i++)for(j=1;j<10;j++){ string o=""; for(k=0;k<m;k++){ P t=g[j][k]; o+=char(t.x|(t.y<<3)); } ans=ans+dfs(i,ans.x,o); } printf("%lld\n",ans.y); } }