[BZOj]1559 [JSOI2009] 密码 AC自动机 + 状压DP

2023-10-21 01:30

本文主要是介绍[BZOj]1559 [JSOI2009] 密码 AC自动机 + 状压DP,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1559: [JSOI2009]密码

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 785   Solved: 266
[ Submit][ Status][ Discuss]

Description

Input

Output

Sample Input

10 2
hello
world

Sample Output

2
helloworld
worldhello

HINT

Source

JSOI2009Day1

[ Submit][ Status][ Discuss]

HOME   Back

  好久没做过AC自动机... 感觉AC自动机主要考察的就是AC自动机上DP或者fail树上搞数据结构?

  首先这道题是字符串, 其次还要构造包含所有串的一个串... 这样的匹配会自然而然的想到字符串里的自动机以来识别, 匹配. 要统计方案数的话, 方案数又在longlong范围内, 那就是很大咯? 那么说明除了数学计数就是DP统计嘛. 数学计数什么的想了一下情况多而且极不好做... 当然是考虑DP啦. 看一下数据范围可以想到状压. 而这种包含匹配一般是用AC自动机来完成... 那么AC自动机上状压DP这个思路就想出来咯.

  设f[i][j][k]表示走了L步走到j节点, k是一个二进制状态表示哪些串已经被包含了.  这样转移显然, 而且答案很好统计, 就是所有的f[L][j][(1 << n) -1]. 当然由AC自动机可以转移到fail, 为了方便转移, 我们当然还是用补全AC自动机啦.

  实际上这道题难就难在输出方案上, 毕竟DP比较naive. 想了半天没怎么想出来. 看了网上的某一种做法. 我们可以注意到只输出不超过42, 这个东西肯定很悬疑啊... 我们想我们最不好处理的就是包含完所有串之后填充L的随机数字, 要是没有这个随机数字就好了... 要是有一个随机数字的话, 他可以是26个字母之一, 无论要包含多少串, 这个数字至少都可以前后放, 那么就有26 * 2种>42. 这就很nice, 这说明要输出方案的话就不会存在随机数字.

  同时这也说明相邻字符串要紧凑, 比如aba和ac要成abac而不是abaac. 因为如果abaac可以的话, 那么abac就可以, 这就流出空位给随机数字了. 所以可以预处理出两个字符串紧凑在一起是什么样, 然后枚举这10个字符串的排列, 每次判断紧凑排列之后长度是不是L即可, 排列是10!所以说复杂度不会爆.  不过wys跟我说实际上可以通过dp转移倒推, 于是我选择了wys的方案...

  注意要去重和包含关系.  

#include<bits/stdc++.h>
using namespace std;
queue<int> q;
bool wr[15];
long long ans;
int n, m, tot, num, cnt, lim, L;
char str[15][15], anstr[44][30], chars[100];
int isend[110], c[110][30], wh[110], fail[110], id[110];
long long f[27][110][1 << 11];
inline void insert(char *ss) {int p = 0;for (int i = 0; ss[i]; ++ i) {int index = ss[i] - 'a';if (!c[p][index]) c[p][index] = ++ tot, wh[tot] = index;p = c[p][index];}isend[p] = ++ cnt;
}
inline bool inside(char *s1, char *s2) {int len1 = strlen(s1), len2 = strlen(s2);if (len1 < len2) return false;for (int i = 0, j, tmp; i < len1; ++ i) {for (j = 0, tmp = i ;j < len2 && tmp < len1; ++ j, ++ tmp)if (s1[tmp] != s2[j]) break;if (j == len2) return true;}return false;
}
inline void prework() {for (int i = 1; i <= m; ++ i)for (int j = 1; j <= m; ++ j) {if (i == j) continue;if (!strcmp(str[i], str[j])) wr[max(i, j)] = true;else if (inside(str[i], str[j])) wr[j] = true;}for (int i = 1; i <= m; ++ i)if (!wr[i]) {++ n;for (int j = 0; j < 11; ++ j) str[n][j] = str[i][j];}for (int i = 1; i <= n; ++ i) insert(str[i]);
}
inline void build_fail() {for (int i = 0; i < 26; ++ i)if (c[0][i]) q.push(c[0][i]);while (!q.empty()) {int u = q.front(); q.pop();for (int i = 0; i < 26; ++ 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];}}
}
inline void dp() {f[0][0][0] = 1;lim = 1 << n;for (int i = 0; i < L; ++ i)for (int j = 0; j <= tot; ++ j)for (int k = 0; k < lim; ++ k) {if (!f[i][j][k]) continue;for (int s = 0, who; s < 26; ++ s) {who = (isend[c[j][s]]) ? (1 << (isend[c[j][s]] - 1)) : 0;f[i + 1][c[j][s]][k | who] += f[i][j][k];}}
}
void dfs(int now, int state, int res) {if (!res) {num ++;for (int i = 0; i < L; ++ i)anstr[num][i] = chars[i];anstr[num][L] = 0;// then it can printf("%s, ...)return;}chars[res - 1] = wh[now] + 'a';int x = (isend[now]) ? (1 << (isend[now] - 1)) : 0;for (int i = 0; i <= tot; ++ i)if (f[res - 1][i][state ^ x] && c[i][wh[now]] == now)dfs(i, state ^ x, res - 1);
}
inline bool cmp(const int &x, const int &y) {for (int i = 0; i < L; ++ i)if (anstr[x][i] != anstr[y][i])return anstr[x][i] < anstr[y][i];return false;
}
inline void solve() {for (int i = 0; i <= tot; ++ i)if (f[L][i][lim - 1])ans += f[L][i][lim - 1];printf("%lld\n", ans);if (ans > 42) return;for (int i = 0; i <= tot; ++ i)if (f[L][i][lim - 1]) dfs(i, lim - 1, L);for (int i = 1; i <= ans; ++ i) id[i] = i;sort(id + 1, id + ans + 1, cmp);for (int i = 1; i <= ans; ++ i)printf("%s\n", anstr[id[i]]);
}
int main() {scanf("%d%d", &L, &m);for (int i = 1; i <= m; ++ i) scanf("%s", str[i]);prework(), build_fail(), dp(), solve();return 0;
}


这篇关于[BZOj]1559 [JSOI2009] 密码 AC自动机 + 状压DP的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/251007

相关文章

Java中的密码加密方式

《Java中的密码加密方式》文章介绍了Java中使用MD5算法对密码进行加密的方法,以及如何通过加盐和多重加密来提高密码的安全性,MD5是一种不可逆的哈希算法,适合用于存储密码,因为其输出的摘要长度固... 目录Java的密码加密方式密码加密一般的应用方式是总结Java的密码加密方式密码加密【这里采用的

mysql重置root密码的完整步骤(适用于5.7和8.0)

《mysql重置root密码的完整步骤(适用于5.7和8.0)》:本文主要介绍mysql重置root密码的完整步骤,文中描述了如何停止MySQL服务、以管理员身份打开命令行、替换配置文件路径、修改... 目录第一步:先停止mysql服务,一定要停止!方式一:通过命令行关闭mysql服务方式二:通过服务项关闭

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

uva 10154 DP 叠乌龟

题意: 给你几只乌龟,每只乌龟有自身的重量和力量。 每只乌龟的力量可以承受自身体重和在其上的几只乌龟的体重和内。 问最多能叠放几只乌龟。 解析: 先将乌龟按力量从小到大排列。 然后dp的时候从前往后叠,状态转移方程: dp[i][j] = dp[i - 1][j];if (dp[i - 1][j - 1] != inf && dp[i - 1][j - 1] <= t[i]

uva 10118 dP

题意: 给4列篮子,每次从某一列开始无放回拿蜡烛放入篮子里,并且篮子最多只能放5支蜡烛,数字代表蜡烛的颜色。 当拿出当前颜色的蜡烛在篮子里存在时,猪脚可以把蜡烛带回家。 问最多拿多少只蜡烛。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cs

uva 10069 DP + 大数加法

代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <cl