题目链接:BZOJ - 1559

题目分析

将给定的串建成AC自动机,然后在AC自动机上状压DP。

转移边就是Father -> Son 或 Now -> Fail。

f[i][j][k] ,表示到了字符串第 i 位,在AC自动机的第 j 个节点上,状态为 k 的方案数。

状态 k 是一个二进制压缩的,表示已经包含了哪些给定串的整数。

然后...输出方案....这个太麻烦了...我是从最后状态DFS向前反推。

另外的问题是我写的AC自动机DP无法正确处理给定串存在串 A 包含串 B 的情况,所以我必须将被其他给定串包含的给定串忽略。

要去掉所有被其他某个给定串包含的给定串。需要在建完Fail之后从每个节点向Fail一直走到Root,将一路上的所有节点都设定为不是给定串。

当然要记录一下某个节点沿Fail到Root的路径已经被处理的话就 break。

我刚开始写的代码只能处理一个串是另一个串前缀或后缀的情况,被包含在中间的情况不能处理,但是仍然在BZOJ AC了。

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue> using namespace std; const int MaxL = 15, MaxM = 15, MaxC = 27, MaxN = 25 + 5, MaxNode = 255 + 5; typedef long long LL; int n, m, l, Index, Tot, MT, Top; LL Ans;
LL f[MaxN][MaxNode][1024 + 15]; char S[MaxL]; struct Str
{
char str[MaxN];
} Sol[50], DS; bool Cmp(Str s1, Str s2)
{
for (int i = 0; i < n; ++i)
{
if (s1.str[i] == s2.str[i]) continue;
return s1.str[i] < s2.str[i];
}
return false;
} struct Trie
{
int Num, ID, c, isStr;
bool Ed;
Trie *Child[MaxC], *Fail;
} TA[MaxNode], *P = TA, *Root, *Zero; void Insert(char *S, int l)
{
Trie *Now = Root;
int t;
for (int i = 1; i <= l; ++i)
{
t = S[i] - 'a';
if (Now -> Child[t] == NULL)
{
++P; P -> Num = 0; P -> ID = ++Tot; P -> c = t;
Now -> Child[t] = P;
}
Now = Now -> Child[t];
}
Now -> isStr = 1;
} void Init_AC()
{
Index = 0; Tot = 0;
Zero = P; //ID : 0
Root = ++P; Root -> ID = ++Tot; //ID : 1
for (int i = 0; i < 26; ++i) Zero -> Child[i] = Root;
Zero -> Fail = NULL;
for (int i = 0; i < 26; ++i) Root -> Child[i] = NULL;
Root -> Fail = Zero;
} queue<Trie *> Q; void Build_Fail()
{
while (!Q.empty()) Q.pop();
Q.push(Root);
Trie *Now, *Temp;
while (!Q.empty())
{
Now = Q.front(); Q.pop();
for (int i = 0; i < 26; ++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 (Trie *j = TA; ; ++j)
{
Temp = j -> Fail;
while (Temp != NULL && Temp != Root && Temp != Zero)
{
if (Temp -> Ed) break;
Temp -> Ed = true;
Temp -> isStr = -1;
Temp = Temp -> Fail;
}
if (j == P) break;
}
for (Trie *j = TA; ; ++j)
{
if (j -> isStr == 1) j -> Num = ++Index;
else j -> Num = 0;
if (j == P) break;
}
} void DP()
{
f[0][1][0] = 1;
MT = (1 << Index) - 1;
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= Tot; ++j)
for (int k = 0; k <= MT; ++k)
if (f[i][j][k])
{
for (int t = 0; t < 26; ++t)
{
if (TA[j].Child[t] -> Num == 0) f[i + 1][TA[j].Child[t] -> ID][k] += f[i][j][k];
else f[i + 1][TA[j].Child[t] -> ID][k | (1 << (TA[j].Child[t] -> Num - 1))] += f[i][j][k];
}
}
Ans = 0;
for (int i = 1; i <= Tot; ++i) Ans += f[n][i][MT];
} void DFS(int l, int x, int y, int t)
{
//printf("Begin %d %d %d %c\n", l, x, y, t + 'a');
DS.str[l - 1] = 'a' + t;
if (l == 1)
{
Sol[++Top] = DS;
return;
}
for (int i = 1; i <= Tot; ++i)
if (f[l - 1][i][y] && TA[i].Child[t] -> ID == x)
{
if (i == 1) for (int j = 0; j < 26; ++j) DFS(l - 1, i, y, j);
else DFS(l - 1, i, y, TA[i].c);
}
int yy;
if (TA[x].Num != 0)
{
yy = y - (1 << (TA[x].Num - 1));
for (int i = 1; i <= Tot; ++i)
if (f[l - 1][i][yy] && TA[i].Child[t] -> ID == x)
{
if (i == 1) for (int j = 0; j < 26; ++j) DFS(l - 1, i, yy, j);
else DFS(l - 1, i, yy, TA[i].c);
}
}
//printf("End %d %d %d %c\n", l, x, y, t + 'a');
} void Get_Sol()
{
Top = 0;
for (int i = 1; i <= Tot; ++i)
if (f[n][i][MT])
{
if (i == 1)
{
for (int j = 0; j < 26; ++j)
DFS(n, i, MT, j);
}
else DFS(n, i, MT, TA[i].c);
}
} int main()
{
scanf("%d%d", &n, &m);
Init_AC();
Index = 0;
for (int i = 1; i <= m; ++i)
{
scanf("%s", S + 1);
l = strlen(S + 1);
Insert(S, l);
}
Build_Fail();
DP();
printf("%lld\n", Ans);
if (Ans <= 42)
{
Get_Sol();
sort(Sol + 1, Sol + Ans + 1, Cmp);
for (int i = 1; i <= Ans; ++i)
{
Sol[i].str[n] = 0;
printf("%s\n", Sol[i].str);
}
}
return 0;
}

  

[BZOJ 1559] [JSOI2009] 密码 【AC自动机DP】的更多相关文章

  1. BZOJ 1559: [JSOI2009]密码( AC自动机 + 状压dp )

    建AC自动机后, dp(x, y, s)表示当前长度为x, 在结点y, 包括的串的状态为s的方案数, 转移就在自动机上走就行了. 对于输出方案, 必定是由给出的串组成(因为<=42), 所以直接 ...

  2. BZOJ 1559 JSOI2009 密码 状压dp+AC自动机+搜索

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1559 分析: 这个题意真的是很**啊!!!直接说每一个字符串至少出现一次不就好了吗... ...

  3. BZOJ1559[JSOI2009]密码——AC自动机+DP+搜索

    题目描述 输入 输出 样例输入 10 2 hello world 样例输出 2 helloworld worldhello 提示 这题算是一个套路题了,多个串求都包含它们的长为L的串的方案数. 显然是 ...

  4. [JSOI2009]密码 [AC自动机]

    题面 bzoj luogu 首先看到这题就知道随便暴枚 只要是多项式算法都能过 先常规建AC自动机 注意被别的单词包含的单词没有存在的价值 剩余单词状压 大力dp f[长度][节点编号][状态] \( ...

  5. [BZOJ1559][JSOI2009]密码(AC自动机)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1559 2009年的省选题虽然比起现在简单了不少,但对我来说还是很有挑战性的. 首先对于这种多串匹配问 ...

  6. [bzoj] 1030 文本生成器 || AC自动机+dp

    原题 给出n个字符串,求随机生成一个m长度的字符串,有多少个是可辨识的(即出现了n个字符串中的任意字符串) 正难则反 求有多少个不可辨识的,26^m-不可辨识即为答案 f[i][j]表示填到第i个字符 ...

  7. [BZOJ 3530] [Sdoi2014] 数数 【AC自动机+DP】

    题目链接:BZOJ - 3530 题目分析 明显是 AC自动机+DP,外加数位统计. WZY 神犇出的良心省选题,然而去年我太弱..比现在还要弱得多.. 其实现在做这道题,我自己也没想出完整解法.. ...

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

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

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

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

随机推荐

  1. 测试cnblogs是否存在链接引用的bug

    如果来源页弹出了一个对话框,或者跳转到了本页面,说明来源页面存在链接引用的bug

  2. Debian 8 编译安装nginx 1.8

    1.安装编译环境 apt-get install build-essential apt-get install gcc make apt-get install libpcre+* apt-get ...

  3. 用Eclipse+xdebug调试PHP总是在首行自动断点解决方法

    问题描述: 使用Eclipse+PDT+xdebug调试PHP程序时,总是在程序的第一行(首行)自动断点,不方便调试. 解决方法: 分别在下面3个位置配置,取消 Break at First Line ...

  4. Eclipse项目 迁移到 Intellj IDEA

    自从用了Intellj IDEA,很多项目都想迁移到Intellj上面去开发  鉴于我们的大部分项目都是基于Maven构建的,所以就可以利用maven的命令来做这个事情.     1.选择一个ecli ...

  5. 黑马程序员- IO(Input- Output)(一)

    ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- API包: Java.io.* 缘来: java通过操作数据对象是通过流的方式来创建的 作用: ...

  6. SQL学习:主键,外键,主键表,外键表,数据库的表与表之间的关系;

    在数据库的学习中,对于一个表的主键和外键的认识是非常重要的. 主键:在一个表中,能唯一的表示一个事物(或者一条记录)的字段,我们称之为主键 注意: 主键的设置可以不只是用一个字段,也可以用若干个字段的 ...

  7. SQL Server 2008文件与文件组的关系

    此文章主要向大家讲述的是SQL Server 2008文件与文件组,其中包括文件和文件组的含义与关系,文件.文件组在实践应用中经常出现的问题,查询文件组和文件语句与MSDN官方解释等相关内容的介绍. ...

  8. AngularJS track by $index引起的思考

    今天写了一段程序,只是一个简答的table数据绑定,但是绑定select的数据之后,发现ng-change事件失去了效果,不知道什么原因. 主要用到的代码如下: <div id="ri ...

  9. WampServer修改MySQL密码

    WampServer安装后密码是空的,需要设置一下 一般有两种方式: 一是通过phpMyAdmin直接修改: 二是使用WAMP的MySql控制台修改. 第一种: ①在phpMyAdmin界面中点击[用 ...

  10. 《C++ Primer》P314中使用insert重写单词统计程序的扩展

    编写程序统计并输出所读入的单词出现的次数 想与习题10-1相结合,也就是先输入几组 map<string, int>类型,存入vector中. 再输入单词word,如果已经存在则在key对 ...