[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

相关文章

Python从零打造高安全密码管理器

《Python从零打造高安全密码管理器》在数字化时代,每人平均需要管理近百个账号密码,本文将带大家深入剖析一个基于Python的高安全性密码管理器实现方案,感兴趣的小伙伴可以参考一下... 目录一、前言:为什么我们需要专属密码管理器二、系统架构设计2.1 安全加密体系2.2 密码强度策略三、核心功能实现详解

SpringSecurity 认证、注销、权限控制功能(注销、记住密码、自定义登入页)

《SpringSecurity认证、注销、权限控制功能(注销、记住密码、自定义登入页)》SpringSecurity是一个强大的Java框架,用于保护应用程序的安全性,它提供了一套全面的安全解决方案... 目录简介认识Spring Security“认证”(Authentication)“授权” (Auth

Oracle登录时忘记用户名或密码该如何解决

《Oracle登录时忘记用户名或密码该如何解决》:本文主要介绍如何在Oracle12c中忘记用户名和密码时找回或重置用户账户信息,文中通过代码介绍的非常详细,对同样遇到这个问题的同学具有一定的参... 目录一、忘记账户:二、忘记密码:三、详细情况情况 1:1.1. 登录到数据库1.2. 查看当前用户信息1.

SpringBoot使用Jasypt对YML文件配置内容加密的方法(数据库密码加密)

《SpringBoot使用Jasypt对YML文件配置内容加密的方法(数据库密码加密)》本文介绍了如何在SpringBoot项目中使用Jasypt对application.yml文件中的敏感信息(如数... 目录SpringBoot使用Jasypt对YML文件配置内容进行加密(例:数据库密码加密)前言一、J

MySQL9.0默认路径安装下重置root密码

《MySQL9.0默认路径安装下重置root密码》本文主要介绍了MySQL9.0默认路径安装下重置root密码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们... 目录问题描述环境描述解决方法正常模式下修改密码报错原因问题描述mysqlChina编程采用默认安装路径,

MySQL修改密码的四种实现方式

《MySQL修改密码的四种实现方式》文章主要介绍了如何使用命令行工具修改MySQL密码,包括使用`setpassword`命令和`mysqladmin`命令,此外,还详细描述了忘记密码时的处理方法,包... 目录mysql修改密码四种方式一、set password命令二、使用mysqladmin三、修改u

电脑密码怎么设置? 一文读懂电脑密码的详细指南

《电脑密码怎么设置?一文读懂电脑密码的详细指南》为了保护个人隐私和数据安全,设置电脑密码显得尤为重要,那么,如何在电脑上设置密码呢?详细请看下文介绍... 设置电脑密码是保护个人隐私、数据安全以及系统安全的重要措施,下面以Windows 11系统为例,跟大家分享一下设置电脑密码的具体办php法。Windo

数据库oracle用户密码过期查询及解决方案

《数据库oracle用户密码过期查询及解决方案》:本文主要介绍如何处理ORACLE数据库用户密码过期和修改密码期限的问题,包括创建用户、赋予权限、修改密码、解锁用户和设置密码期限,文中通过代码介绍... 目录前言一、创建用户、赋予权限、修改密码、解锁用户和设置期限二、查询用户密码期限和过期后的修改1.查询用

Java中的密码加密方式

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

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

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