题目链接:BZOJ - 3530

题目分析

明显是 AC自动机+DP,外加数位统计。

WZY 神犇出的良心省选题,然而去年我太弱..比现在还要弱得多..

其实现在做这道题,我自己也没想出完整解法..

就想出了个 O(l^3) 的做法:

完全按照数位统计的思想来,先统计长度不足 len 的数字的合法种类数,这个枚举开头,然后 AC 自动机 DP 一下,用 f[i][j] 表示到了第 i 位,在第 j 个节点上的合法数字个数。这样是 O(L^2)。

然后长度等于 n 的部分,就按照数位统计,一位位向后推,然后每次 O(L^2) DP 求,这样总的复杂度是 O(L^3)。

注意不能走包含模式串的节点,首先 Trie 中模式串的节点肯定不能走,其次,如果沿着一个节点的 Fail 链一直向上走,只要链上有一个节点是不能走的,那么这个节点也就不能走了。

所以需要从每个的节点开始,逆着 Fail 的边 DFS 一下,如果当前状态下将经过的点全都 Ban 掉。但是数据好像比较弱,不加这个也是不会出问题的。

但是这样会弄错一种情况:比如有 123, 2 这两个串,然后它就沿着 -> 1 -> 2 一直向后匹配,没有匹配成功 123 ,但是其实已经包含了 2 这个串。

这样的复杂度只有70分。

正解的复杂度是 O(L^2) 的:对于长度不足 len 的数字,还是直接求,O(L^2)。

对于长度等于 len 的数字,我们就直接用一个 O(L^2) 的 DP 求出来,我们把状态增加一维, g[i][j][k] ,前两位是第 i 位,走到第 j 个节点,k 是一个 [0/1] 变量,表示这个状态是否是沿着 n 的每一位走过来。

这样我们就知道了,如果当前状态是依照 n 的每一位走过来,那么下一步就只能转移 [0, n[i+1]] 这些数字。否则可以转移所有的数字。这样就求出了小于等于 n 的所有合法数字。

总的复杂度是 O(L^2) 。

AC自动机+DP的一般做法:f[i][j] 表示走了 i 步,走到了第 j 个节点,有时还需要加第 3 维或更多。

然后 DP 的时候就是 枚举 i ,枚举 j,枚举转移到下一步的字符。

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <queue> using namespace std; const int MaxL = 1500 + 5, MaxNode = 15000 + 5, Mod = 1000000007; int len, l, m, Index, Ans;
int f[MaxL][MaxNode], g[MaxL][MaxNode][2]; char S[MaxL], Ns[MaxL]; struct Trie
{
int x, Idx;
bool Ban;
Trie *Fail, *Child[10];
} TA[MaxNode], *P = TA, *Root, *Zero; Trie* NewNode(int Num = 0)
{
++P;
P -> x = Num;
P -> Idx = ++Index;
P -> Fail = NULL;
P -> Ban = false;
memset(P -> Child, 0, sizeof(P -> Child));
return P;
} void Insert(char *S, int l)
{
int t;
Trie *Now = Root;
for (int i = 1; i <= l; ++i)
{
t = S[i] - '0';
if (Now -> Child[t] == NULL)
Now -> Child[t] = NewNode(t);
Now = Now -> Child[t];
}
Now -> Ban = true;
} struct Edge
{
int v;
Edge *Next;
} E[MaxNode], *PE = E, *Point[MaxNode]; inline void AddEdge(int x, int y)
{
++PE; PE -> v = y;
PE -> Next = Point[x]; Point[x] = PE;
} bool Visit[MaxNode]; void DFS(int x, bool f)
{
Visit[x] = true;
TA[x].Ban = (TA[x].Ban) || f;
for (Edge *j = Point[x]; j; j = j -> Next)
DFS(j -> v, TA[x].Ban);
} queue<Trie *> Q; void Build_Fail()
{
while (!Q.empty()) Q.pop();
Q.push(Root);
Trie *Now;
while (!Q.empty())
{
Now = Q.front(); Q.pop();
AddEdge(Now -> Fail -> Idx, Now -> Idx);
for (int i = 0; i <= 9; ++i)
{
if (Now -> Child[i] == NULL) Now -> Child[i] = Now -> Fail -> Child[i];
else
{
Now -> Child[i] -> Fail = Now -> Fail -> Child[i];
Q.push(Now -> Child[i]);
}
}
}
for (int i = 1; i <= Index; ++i)
if (!Visit[i]) DFS(i, TA[i].Ban);
} void Usual_Disco()
{
memset(f, 0, sizeof(f));
for (int i = 1; i <= 9; ++i)
if (Root -> Child[i] -> Ban == false)
++f[1][Root -> Child[i] -> Idx];
for (int i = 1; i < len; ++i)
for (int j = 1; j <= Index; ++j)
{
if (TA[j].Ban) continue;
Ans = (Ans + f[i][j]) % Mod;
for (int t = 0; t <= 9; ++t)
if (!TA[j].Child[t] -> Ban)
{
f[i + 1][TA[j].Child[t] -> Idx] += f[i][j];
f[i + 1][TA[j].Child[t] -> Idx] %= Mod;
}
}
memset(g, 0, sizeof(g));
for (int i = 1; i < Ns[1] - '0'; ++i)
if (Root -> Child[i] -> Ban == false)
++g[1][Root -> Child[i] -> Idx][0];
if (Root -> Child[Ns[1] - '0'] -> Ban == false)
++g[1][Root -> Child[Ns[1] - '0'] -> Idx][1];
for (int i = 1; i <= len; ++i)
for (int j = 1; j <= Index; ++j)
{
if (TA[j].Ban) continue;
if (i == len)
{
Ans = (Ans + g[i][j][0]) % Mod;
Ans = (Ans + g[i][j][1]) % Mod;
continue;
}
for (int t = 0; t <= 9; ++t)
if (!TA[j].Child[t] -> Ban)
{
g[i + 1][TA[j].Child[t] -> Idx][0] += g[i][j][0];
g[i + 1][TA[j].Child[t] -> Idx][0] %= Mod;
}
for (int t = 0; t < Ns[i + 1] - '0'; ++t)
if (!TA[j].Child[t] -> Ban)
{
g[i + 1][TA[j].Child[t] -> Idx][0] += g[i][j][1];
g[i + 1][TA[j].Child[t] -> Idx][0] %= Mod;
}
if (!TA[j].Child[Ns[i + 1] - '0'] -> Ban)
{
g[i + 1][TA[j].Child[Ns[i + 1] - '0'] -> Idx][1] += g[i][j][1];
g[i + 1][TA[j].Child[Ns[i + 1] - '0'] -> Idx][1] %= Mod;
}
}
} int main()
{
scanf("%s", Ns + 1);
len = strlen(Ns + 1);
scanf("%d", &m);
Root = NewNode();
Zero = NewNode();
Root -> Fail = Zero;
for (int i = 0; i <= 9; ++i) Zero -> Child[i] = Root;
for (int i = 1; i <= m; ++i)
{
scanf("%s", S + 1);
l = strlen(S + 1);
Insert(S, l);
}
Build_Fail();
Usual_Disco();
Ans %= Mod;
cout << Ans << endl;
return 0;
}

  

[BZOJ 3530] [Sdoi2014] 数数 【AC自动机+DP】的更多相关文章

  1. [BZOJ 1559] [JSOI2009] 密码 【AC自动机DP】

    题目链接:BZOJ - 1559 题目分析 将给定的串建成AC自动机,然后在AC自动机上状压DP. 转移边就是Father -> Son 或 Now -> Fail. f[i][j][k] ...

  2. 【BZOJ】4861: [Beijing2017]魔法咒语 AC自动机+DP+矩阵快速幂

    [题意]给定n个原串和m个禁忌串,要求用原串集合能拼出的不含禁忌串且长度为L的串的数量.(60%)n,m<=50,L<=100.(40%)原串长度为1或2,L<=10^18. [算法 ...

  3. BZOJ 1212: [HNOI2004]L语言 [AC自动机 DP]

    1212: [HNOI2004]L语言 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1367  Solved: 598[Submit][Status ...

  4. BZOJ 1030: [JSOI2007]文本生成器 [AC自动机 DP]

    1030: [JSOI2007]文本生成器 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 3953  Solved: 1614[Submit][Stat ...

  5. 【BZOJ3530】数数(AC自动机,动态规划)

    [BZOJ3530]数数(AC自动机,动态规划) 题面 BZOJ 题解 很套路的\(AC\)自动机+\(DP\) 首先,如果长度小于\(N\) 就不存在任何限制 直接大力\(DP\) 然后强制限制不能 ...

  6. 洛谷P3311 [SDOI2014]数数 AC自动机+dp

    正解:AC自动机+dp 解题报告: 传送门! 首先看到多串匹配balabala显然想到建个AC自动机? 然后可以用一点儿数位dp的思想地想下(,,,其实并不算QAQ 幸运数可以分为两类:位数<n ...

  7. bzoj 1030 [JSOI2007]文本生成器(AC自动机+DP)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1030 [题意] 给n个小串,随机构造一个长为m的大串,一个串合法当且仅当包含一个或多个 ...

  8. POJ1625 Censored!(AC自动机+DP)

    题目问长度m不包含一些不文明单词的字符串有多少个. 依然是水水的AC自动机+DP..做完后发现居然和POJ2778是一道题,回过头来看都水水的... dp[i][j]表示长度i(在自动机转移i步)且后 ...

  9. HDU2457 DNA repair(AC自动机+DP)

    题目一串DNA最少需要修改几个基因使其不包含一些致病DNA片段. 这道题应该是AC自动机+DP的入门题了,有POJ2778基础不难写出来. dp[i][j]表示原DNA前i位(在AC自动机上转移i步) ...

随机推荐

  1. 一种协程的 C/C++ 实现

    一种协程的 C/C++ 实现 介绍 在前几天接触到了协程的概念,觉得很有趣.因为我可以使用一个线程来实现一个类似多线程的程序,如果使用协程来替代线程,就可以省去很多原子操作和内存栅栏的麻烦,大大减少与 ...

  2. jquery登录验证插件

    最近写了个登录验证的jquery插件,其中功能还不是很完善,需要进一步改进,先放出来看看先. /** * 功能描述:本模块实现普通网站登录验证,以后可以添加二维码验证,以及第三方帐号登录验证 * 关联 ...

  3. HTTP请求、响应报文格式

    HTTP请求报文格式: HTTP请求报文主要由请求行.请求头部.空行以及请求正文4部分组成 1,请求行由3部分组成,分别为:请求方式,URI(注意这里不是URL)以及协议版本组成,之间由空格分隔 请求 ...

  4. 发布GeoServer后预览提示下载wms文件

    这是因为发布的图层有中文所导致的,只需修改tomcat的server.xml文件 <Connector port="8080" protocol="HTTP/1.1 ...

  5. JS插件-日期

    原文出处 源码下载 原文出处 源码下载

  6. SQLSERVER一些公用DLL的作用解释

    如果你的SQLSERVER安装在C盘的话,下面的路径就是相应SQLSERVER版本的公用DLL的存放路径 SQL2005 C:\Program Files\Microsoft SQL Server\9 ...

  7. eclipse和android studio导入工程的错误

    eclipse中导入工程,需要注意导入的工程是什么,android 工程和java工程是有区别的.如果导入错误了,调起来也比较麻烦.因为入口错了呀. 特别在android studio工程,从其它人的 ...

  8. 安卓获取Assets目录下的资源

    获取Assets目录下的资源 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 ! ...

  9. HTTP协议认识

    一.HTTP协议概念 HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传送协议 HTTP是一个应用层协议,由请求和响应 ...

  10. sublimeText3中 less2css error: `lessc` is not available的解决办法

    昨天在sublimeText3中安装了less 以及 less2css插件,但是在编译保存的时候总是出现错误: less2css error: `lessc` is not available 就go ...