BZOJ3530[Sdoi2014]数数——AC自动机+数位DP

时间:2022-12-11 23:21:11

题目描述

我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串。例如当S=(22,333,0233)时,233是幸运数,2333、20233、3223不是幸运数。
    给定N和S,计算不大于N的幸运数个数。

输入

输入的第一行包含整数N。
    接下来一行一个整数M,表示S中元素的数量。
    接下来M行,每行一个数字串,表示S中的一个元素。

输出

输出一行一个整数,表示答案模109+7的值。

样例输入

20
3
2
3
14

样例输出

14

提示

下表中l表示N的长度,L表示S中所有串长度之和。

1 < =l < =1200 , 1 < =M < =100 ,1 < =L < =1500

这道题和bzoj1030比较像,建议先做一下那道题。虽然是一道AC自动机的题但重点是dp,因为不只有位数限制,每一位还有限制数值,所以不能只用f[i][j]表示第i位走到了j节点。因为有限制值所以我们不妨在前面再加一维变成f[k][i][j](k=0或k=1),f[0][i][j]表示第i为走到j节点需要受限制(即前几位都等于每一位限制值),f1[1][i][j]则表示第i位走到j节点不受限制(即前几位有至少一位低于限制值)。当枚举f[0][i][j]时如果j节点所代表的数字小于第i位的限制值,那就可以转移到f[1][i+1][x](x为j的子节点).对于f[0][i][j],因为这一位受限制,所以下一位也要相应受限制,即f[0][i][j]转移到f[0][i+1][x].对于f[1][i][j],因为这一位不受限制,下一位一定不受限制,所以从f[1][i][j]转移到f[1][i+1][x]。

最后附上代码。

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
struct tree
{
int fail;
int vis[11];
int end;
}a[1600];
char s[1600];
char t[1250];
int cnt;
int n;
int m;
long long ans;
long long f[3][1250][1600];
int mod=1e9+7;
void build(char *s)
{
int l=strlen(s);
int now=0;
for(int i=0;i<l;i++)
{
int x=s[i]-'0';
if(!a[now].vis[x])
{
a[now].vis[x]=++cnt;
}
now=a[now].vis[x];
}
a[now].end++;
}
void getfail()
{
queue<int>q;
for(int i=0;i<10;i++)
{
if(a[0].vis[i]!=0)
{
a[a[0].vis[i]].fail=0;
q.push(a[0].vis[i]);
}
}
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0;i<10;i++)
{
if(!a[now].vis[i])
{
a[now].vis[i]=a[a[now].fail].vis[i];
continue;
}
a[a[now].vis[i]].fail=a[a[now].fail].vis[i];
a[a[now].vis[i]].end|=a[a[a[now].fail].vis[i]].end;
q.push(a[now].vis[i]);
}
}
}
int main()
{
scanf("%s",t+1);
m=strlen(t+1);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
build(s);
}
getfail();
for(int i=0;i<m;i++)
{
for(int j=0;j<=cnt;j++)
{
if(!j)
{
if(!i)
{
int x=t[i+1]-'0';
for(int k=1;k<x;k++)
{
if(!a[a[j].vis[k]].end)
{
f[1][i+1][a[j].vis[k]]+=1;
f[1][i+1][a[j].vis[k]]%=mod;
}
}
if(!a[a[j].vis[x]].end)
{
f[0][i+1][a[j].vis[x]]+=1;
f[0][i+1][a[j].vis[x]]%=mod;
}
}
else
{
for(int k=1;k<=9;k++)
{
if(!a[a[j].vis[k]].end)
{
f[1][i+1][a[j].vis[k]]+=1;
f[1][i+1][a[j].vis[k]]%=mod;
}
}
}
}
if(f[0][i][j])
{
int x=t[i+1]-'0';
for(int k=0;k<x;k++)
{
if(!a[a[j].vis[k]].end)
{
f[1][i+1][a[j].vis[k]]+=f[0][i][j];
f[1][i+1][a[j].vis[k]]%=mod;
}
}
if(!a[a[j].vis[x]].end)
{
f[0][i+1][a[j].vis[x]]+=f[0][i][j];
f[0][i+1][a[j].vis[x]]%=mod;
}
}
if(f[1][i][j])
{
for(int k=0;k<=9;k++)
{
if(!a[a[j].vis[k]].end)
{
f[1][i+1][a[j].vis[k]]+=f[1][i][j];
f[1][i+1][a[j].vis[k]]%=mod;
}
}
}
}
}
for(int i=0;i<=cnt;i++)
{
ans+=f[0][m][i];
ans%=mod;
ans+=f[1][m][i];
ans%=mod;
}
printf("%lld",ans);
}

BZOJ3530[Sdoi2014]数数——AC自动机+数位DP的更多相关文章

  1. 【HDU3530】 &lbrack;Sdoi2014&rsqb;数数 (AC自动机&plus;数位DP)

    3530: [Sdoi2014]数数 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 682  Solved: 364 Description 我们称一 ...

  2. 【JZOJ3624】【SDOI2014】数数&lpar;count&rpar; AC自动机&plus;数位dp

    题面 100 容易想到使用AC自动机来处理禁忌子串的问题: 然后在自动机上数位dp,具体是: \(f_{i,j,0/1}\)表示填了\(i\)位,当前在自动机的第\(j\)个结点上,\(0\)表示当前 ...

  3. 【bzoj3530】&lbrack;Sdoi2014&rsqb;数数 AC自动机&plus;数位dp

    题目描述 我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串.例如当S=(22,333,0233)时,233是幸运数,2333.20233.3223不是幸运 ...

  4. BZOJ 3530 &lbrack;SDOI2014&rsqb;数数 &lpar;Trie图&sol;AC自动机&plus;数位DP&rpar;

    题目大意:略 裸的AC自动机+数位DP吧... 定义f[i][x][0/1]表示已经匹配到了第i位,当前位置是x,0表示没到上限,1到上限,此时数是数量 然而会出现虚拟前导零,即前几位没有数字的情况, ...

  5. BZOJ3530&colon;&lbrack;SDOI2014&rsqb;数数&lpar;AC自动机&comma;数位DP&rpar;

    Description 我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串.例如当S=(22,333,0233)时,233是幸运数,2333.20233.3 ...

  6. BZOJ 3530&colon; &lbrack;Sdoi2014&rsqb;数数 &lbrack;AC自动机 数位DP&rsqb;

    3530: [Sdoi2014]数数 题意:\(\le N\)的不含模式串的数字有多少个,\(n=|N| \le 1200\) 考虑数位DP 对于长度\(\le n\)的,普通套路DP\(g[i][j ...

  7. &lbrack;SDOI2014&rsqb;数数 --- AC自动机 &plus; 数位DP

    [SDOI2014]数数 题目描述: 我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串. 例如当S=(22,333,0233)时,233是幸运数,2333 ...

  8. P3311 &lbrack;SDOI2014&rsqb;数数 AC自动机&plus;数位DP

    题意 给定一个正整数N和n个模式串,问不大于N的数字中有多少个不包含任意模式串,输出对\(1e^9+7\)取模后的答案. 解题思路 把所有模式串都加入AC自动机,然后跑数位DP就好了.需要注意的是,这 ...

  9. HDU-4518 吉哥系列故事——最终数 AC自动机&plus;数位DP

    题意:如果一个数中的某一段是长度大于2的菲波那契数,那么这个数就被定义为F数,前几个F数是13,21,34,55......将这些数字进行编号,a1 = 13, a2 = 21.现给定一个数n,输出和 ...

随机推荐

  1. SQL Server-聚焦NOT IN VS NOT EXISTS VS LEFT JOIN&period;&period;&period;IS NULL性能分析(十八)

    前言 本节我们来综合比较NOT IN VS NOT EXISTS VS LEFT JOIN...IS NULL的性能,简短的内容,深入的理解,Always to review the basics. ...

  2. POJ 2029 Get Many Persimmon Trees

    Get Many Persimmon Trees Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 3243 Accepted: 2 ...

  3. MSP430x1&lowbar;4&lowbar;6x之问题总结

    01:MSP430端口上电复位的初始值是不确定的:所以使用是都要初始化:比如加下面的语句或者加你使用的端口就行了:  /*下面六行程序关闭所有的IO口*/    P1DIR = 0XFF;P1OUT ...

  4. PHP函数前面的&commat;。

    @是可以屏蔽函数执行过程中遇到问题而产生的一些错误.警告信息,这样用户就看不到程序的出错信息.这样除了用户界面会友好一些外,更重要的是安全性,因为屏蔽了出错文件的路径等信息. 比如说这个: for($ ...

  5. OC - 26&period;CAAnimationGroup

    概述 简介 CAAnimationGroup又称组动画或动画组 将多个动画放到动画组中,并赋值给layer的animations属性,动画组中所有动画就会并发执行 注意事项 动画组中的动画不会被压缩, ...

  6. python中如何单独测试一个函数的作用

    #!/usr/bin/python import os def get_env_varible(key): return os.getenv(key) if __name__ == '__main__ ...

  7. CC&plus;&plus;初学者编程教程&lpar;7&rpar; 搭建Windows EclipseCCPP软件开发环境

    1根据电脑是32位还是64位来选择工具 2 查看电脑是64位 3 管理员身份运行这个文件 4 安装JDK64位 5. 下一步 6 开始安装 7 安装JAVA 8 安装进行时 9 安装成功 10解压缩 ...

  8. 8259A工作原理描述

    通过初始化编程向8259A写入相应的初始化命令ICW,可以使芯片处于一个规定的基本工作方式,并在此方式下进行工作.8259A的初始化命令字共有4个ICW1-ICW4,进行初始化时要求ICW1-ICW4 ...

  9. 6&period; Java 加解密技术系列之 3DES

    Java 加解密技术系列之 3DES 序 背景 概念 原理 代码实现 结束语 序 上一篇文章讲的是对称加密算法 — — DES,这篇文章打算在 DES 的基础上,继续多讲一点,也就是 3 重 DES ...

  10. scrapy 基础使用以及错误方案

    原先用的是selenium(后面有时间再写),这是第一次使用scrapy这个爬虫框架,所以记录一下这个心路历程,制作简单的爬虫其实不难,你需要的一般数据都可以爬取到. 下面是我的目录,除了main.p ...