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);
  }
}

  

posted @ 2016-10-24 16:49 Claris 阅读(...) 评论(...) 编辑 收藏