[SDOI2014]数数

题解:

做过ac自动机上dp的这题应该就很容易想到了

首先在ac自动机上搞dp

表示当前考虑了i位,在自动机的j位上

然后转移就可以了

考虑限制

显然是一个数位dp

考虑位数小于n显然满足要求

考虑位数等于n

令f[i][j][0/1]表示前i位,自动机j上,与限制是否重合 然后枚举转移就行了

另外就是对于位数比它少的再做一次(否则的话有前导零可能会让答案变小)

代码:

 

#include <bits/stdc++.h>
using namespace std;
#define N 2000
#define mo 1000000007
char cc[N];
int x1[N],x2[N],c[5000][11],val[5000],fail[5000];
int dp[1300][5000][2],cnt;
bool tt=1;
void insert(char *cc)
{
  int len=strlen(cc),now=0;
  for (int i=0;i<len;i++)
  {
    int v=cc[i]-'0';
    if (!c[now][v]) c[now][v]=++cnt;
    now=c[now][v];
  }
  val[now]=1;
}
queue<int> q;
void build()
{
  for (int i=0;i<=9;i++)
    if (c[0][i]) fail[c[0][i]]=0,q.push(c[0][i]);
  while (!q.empty())
  {
    int u=q.front(); q.pop();
    for (int i=0;i<=9;i++)
    {
      if (c[u][i])
      {
        fail[c[u][i]]=c[fail[u]][i];
        q.push(c[u][i]);
      } else c[u][i]=c[fail[u]][i];
      val[c[u][i]]|=val[c[fail[u]][i]];
    }
  }
}
void plus2(int &x,int y)
{
  x+=y;
  x=x%mo; 
}
bool pd(int x,int y)
{
  if(x==0&&y==0) return(false);
  else return(true);
}
int main()
{
  freopen("noi.in","r",stdin);
  freopen("noi.out","w",stdout);
  std::ios::sync_with_stdio(false);
  cin>>cc;
  int n,m,len=strlen(cc);
  n=len;
  for (int i=0;i<len;i++)
    x1[i]=cc[i]-'0';
  cin>>m;
  for (int i=1;i<=m;i++)
  {
    cin>>cc;
    insert(cc);
  }
  build();
  dp[0][0][0]=1;
  for (int i=0;i<=n-1;i++)
    for (int j=0;j<=cnt;j++)
   //   if (dp[i][j])
      {
        for (int k=0;k<=9;k++)
          if (!val[c[j][k]]&&(pd(i,k))) 
            plus2(dp[i+1][c[j][k]][1],dp[i][j][1]);
        for (int k=0;k<=x1[i]-1;k++)
          if (!val[c[j][k]]&&(pd(i,k)))
            plus2(dp[i+1][c[j][k]][1],dp[i][j][0]);
        if (!val[c[j][x1[i]]])
          plus2(dp[i+1][c[j][x1[i]]][0],dp[i][j][0]);
      }
  int ans=0;
  for (int i=0;i<=cnt;i++)
    plus2(ans,dp[n][i][0]),plus2(ans,dp[n][i][1]);
  memset(dp,0,sizeof(dp));
  dp[0][0][0]=1;
  for (int i=0;i<=n-2;i++)
    for (int j=0;j<=cnt;j++)
    {
       for (int k=0;k<=9;k++)
          if (!val[c[j][k]]&&(pd(i,k))) 
            plus2(dp[i+1][c[j][k]][0],dp[i][j][0]);
    }
  for (int i=1;i<=n-1;i++)
    for (int j=0;j<=cnt;j++)
      plus2(ans,dp[i][j][0]);
  cout<<ans;
  return 0;
}

 

posted @ 2018-04-04 20:33  尹吴潇  阅读(175)  评论(0编辑  收藏  举报