题目链接: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. Android(java)学习笔记156:Java虚拟机和Dalvik虚拟机的区别

    Google于2007年底正式发布了Android SDK, 作为 Android系统的重要特性,Dalvik虚拟机也第一次进入了人们的视野.它对内存的高效使用,和在低速CPU上表现出的高性能,确实令 ...

  2. p

    都不知道简历去投什么地方.游戏都卖不出去,又做不出口碑好的.这些人是心存侥幸还是心存坚持. 感觉自己搞不清楚就很难再出发.

  3. 百度编辑器umeditor使用总结

    百度编辑器是一个功能很全.很强大. 百度单张图片上传只能存储在项目下面,而不能独立自定义存储位置,因此重写上传代码 百度文章中的图片是通过base64实现的,直接存储在数据库中 tomcat通过虚拟路 ...

  4. 自己动手实现SharePointList的分页展示

    SharePoin列表里对条目的展示只有上一页下一页,不能够跳转,不能够一次导航到第一页和最后一页. 项目需要,所以对列表的数据展示进行了定制化开发来实现如上的功能. 前端用GridView展示,用L ...

  5. Android在onCreate()中获得控件尺寸

    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceSt ...

  6. ASP.NET页面生命周期总结(2)

    HttpAplicationFactory获取一个HttpApplication对象: 内部:1.如果是第一次请求过来,那么就把global文件编译成一个类型.(后续请求来的,就可以直接获取这个类型) ...

  7. O-C相关-10-动态类型检查

    10-动态类型检查 1.动态绑定 1)OC 中方法的调用不由编译器决定,而由运行时决定 2)OC 中没有方法调用,只有消息接收. 一般称消息为选择器 2.动态类型检查 对象在运行时获得类型的能力称为内 ...

  8. vi 或 vim 常用命令(简单够用了)

    1.vi filename :打开或新建文件,并将光标置于第一行首 2.按下i键:编辑或插入数据3.按下shit+: ->表示可以进行命令输入 4.q! ->表示不保存退出.5.w -&g ...

  9. angularJS--神奇的$scope

    我们在使用angularJS时,$scope对于angularJS是非常重要的,它是angularJS的基础,但$scope到底是什么呢?下面进行一些介绍. 1.$scope是一个普通的js对象 2. ...

  10. 我爆一个托 QQ305242038 电话 18782169971

    这是两个人,一品天下附近的托,qq负责聊天,电话那个负责见面