hdu6086(AC 自动机)
hdu6086
题意
字符串只由 \(01\) 组成,求长度为 \(2L\) 且包含给定的 \(n\) 个子串的字符串的个数(且要求字符串满足 \(s[i] \neq s[|s| - i + 1]\))。
分析
没有想到可以暴力预处理中间那些字符。
官方题解:
如果没有反对称串的限制,直接求一个长度为 \(L\) 的 \(01\) 串满足所有给定串都出现过,那么是一个经典的 AC 自动机的问题,状态 \(f[i][j][S]\) 表示长度为 \(i\),目前在 AC 自动机的节点 \(j\) 上,已经出现的字符串集合为 \(S\) 的方案数,然后直接转移即可,时间复杂度 \(O(2^nL\sum |s|)\)。
然后如果不考虑有串跨越中轴线,那么可以预处理所有正串的 AC 自动机和所有反串(即原串左右翻转)的 AC 自动机,然后从中间向两边 DP,每一次枚举右侧下一个字符是 \(0\) 还是 \(1\),那么另一侧一定是另外一个字符。状态 \(f[i][j][k][S]\) 表示长度为 \(2i\),目前右半边在正串 AC 自动机的节点 \(j\) 上,左半边的反串在反串 AC 自动机的节点 \(k\) 上,已经出现的字符串集合为 \(S\) 的方案数,然后直接转移,时间复杂度 \(O(2^nL(\sum |s|)^2)\)。
现在考虑有串跨越中轴线,可以先爆枚从中间开始左右各 \(\max|s|-1\) 个字符,统计出哪些串以及出现了。对于之后左右扩展出去的字符来说,肯定没有经过的它们的字符串跨越中轴线,因此可以以爆枚的结果为 DP 的初始值,从第 \(\max|s|\) 个字符开始 DP。
时间复杂度 \(O(2^nL(\sum |s|)^2+\max|s|2^{\max|s|})\)。
数组要开成滚动数组,然后爆搜的时候自动机上的状态也要跟着转移。
时限还是很宽松的。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
typedef long long ll;
const int MAXN = 121;
const int MOD = 998244353;
struct Trie {
int root, L, nxt[MAXN][2], fail[MAXN], val[MAXN];
int newnode() {
memset(nxt[L], -1, sizeof nxt[L]);
return L++;
}
void init() {
L = 0;
root = newnode();
memset(val, 0, sizeof val);
memset(fail, 0, sizeof fail);
}
void insert(int id, char S[]) {
int len = strlen(S);
int now = root;
for(int i = 0; i < len; i++) {
int d = S[i] - '0';
if(nxt[now][d] == -1) nxt[now][d] = newnode();
now = nxt[now][d];
}
val[now] |= (1 << id);
}
void build() {
queue<int> Q;
for(int i = 0; i < 2; i++) {
if(nxt[root][i] == -1) nxt[root][i] = 0;
else { fail[nxt[root][i]] = root; Q.push(nxt[root][i]); }
}
while(!Q.empty()) {
int now = Q.front(); Q.pop();
val[now] |= val[fail[now]];
for(int i = 0; i < 2; i++) {
if(nxt[now][i] == -1) nxt[now][i] = nxt[fail[now]][i];
else { fail[nxt[now][i]] = nxt[fail[now]][i]; Q.push(nxt[now][i]); }
}
}
}
int query(char S[], int l, int r) {
int now = root;
int res = 0;
int flg = 0;
int mid = (r - l) / 2 + l;
for(int i = l; i <= r; i++) {
int d = S[i] - '0';
now = nxt[now][d];
res |= val[now];
}
return res;
}
}trie1, trie2;
int n, L, mx;
int dp[2][MAXN][MAXN][64];
void dfs(char s[], int l, int r, int nl, int nr) {
int len = r - l + 1;
if(len / 2 >= mx) {
int tmp = trie2.query(s, l, r);
dp[1][nl][nr][tmp]++;
return;
}
s[l - 1] = '0'; s[r + 1] = '1';
dfs(s, l - 1, r + 1, trie1.nxt[nl][0], trie2.nxt[nr][1]);
s[l - 1] = '1'; s[r + 1] = '0';
dfs(s, l - 1, r + 1, trie1.nxt[nl][1], trie2.nxt[nr][0]);
}
int cnt[64];
int main() {
cnt[0] = 0;
for(int i = 1; i < 64; i++) {
int j = 0;
while(!((i >> j) & 1)) j++;
cnt[i] = cnt[i - (1 << j)] + 1;
}
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &L);
trie1.init();
trie2.init();
mx = 0;
for(int i = 0; i < n; i++) {
char s[22];
scanf("%s", s);
trie2.insert(i, s);
int len = strlen(s);
mx = max(mx, len);
reverse(s, s + len);
trie1.insert(i, s);
}
mx--;
trie1.build();
trie2.build();
memset(dp, 0, sizeof dp);
char s[65];
dfs(s, 23, 22, 0, 0);
int z = 1;
for(int i = mx; i < L; i++, z = !z) {
memset(dp[!z], 0, sizeof dp[!z]);
for(int j = 0; j < trie1.L; j++) {
for(int k = 0; k < trie2.L; k++) {
for(int p = 0; p < (1 << n); p++) {
if(!dp[z][j][k][p]) continue;
for(int q = 0; q < 2; q++) {
int tmp1 = trie1.nxt[j][q], tmp2 = trie2.nxt[k][!q];
(dp[!z][tmp1][tmp2][p | trie1.val[tmp1] | trie2.val[tmp2]] += dp[z][j][k][p]) %= MOD;
}
}
}
}
}
int sum = 0;
for(int i = 0; i < trie1.L; i++) {
for(int j = 0; j < trie2.L; j++) {
sum = (sum + dp[z][i][j][(1 << n) - 1]) % MOD;
}
}
printf("%d\n", sum);
}
return 0;
}
hdu6086(AC 自动机)的更多相关文章
- 【AC自动机】【状压dp】【滚动数组】hdu6086 Rikka with String
给你m个01串,问你有多少个长度为2L的01串,满足前半段倒置取反后等于后半段,并且包含所有的m个01串. 考虑单词完全在中线前面或者后面的情况,直接将单词及其倒置取反插入AC自动机,AC自动机每个结 ...
- 基于trie树做一个ac自动机
基于trie树做一个ac自动机 #!/usr/bin/python # -*- coding: utf-8 -*- class Node: def __init__(self): self.value ...
- AC自动机-算法详解
What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...
- python爬虫学习(11) —— 也写个AC自动机
0. 写在前面 本文记录了一个AC自动机的诞生! 之前看过有人用C++写过AC自动机,也有用C#写的,还有一个用nodejs写的.. C# 逆袭--自制日刷千题的AC自动机攻克HDU OJ HDU 自 ...
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2545 Solved: 1419[Submit][Sta ...
- BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]
3172: [Tjoi2013]单词 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 3198 Solved: 1532[Submit][Status ...
- BZOJ 1212: [HNOI2004]L语言 [AC自动机 DP]
1212: [HNOI2004]L语言 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1367 Solved: 598[Submit][Status ...
- [AC自动机]【学习笔记】
Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)To ...
- AC自动机 HDU 3065
大概就是裸的AC自动机了 #include<stdio.h> #include<algorithm> #include<string.h> #include< ...
随机推荐
- 原生ajax方法封装
/** * @function ajax request * @fields ajaxName:请求名称,method:请求方法,headers:setRequestHeader自定义部分,url:接 ...
- MySQL:BlackHole
MySQL:BlackHole 顾名思义BlackHole就是黑洞,只有写入没有输出.现在就来实验一下吧 首先查看一下MySQL支持的存储引擎 mysql> show engines;+---- ...
- Codeforces Round #520 (Div. 2) A. A Prank
A. A Prank time limit per test 1 second memory limit per test 256 megabytes 题目链接:https://codefo ...
- namenode磁盘满引发recover edits文件报错
前段时间公司hadoop集群宕机,发现是namenode磁盘满了, 清理出部分空间后,重启集群时,重启失败. 又发现集群Secondary namenode 服务也恰恰坏掉,导致所有的操作log持续写 ...
- Join to domain powershell script
Join to domain powershell script $username = "domain\admin" $Password = "xxxxxxxx&quo ...
- dbcp基本配置和重连配置
转载自:http://agapple.iteye.com/blog/772507 最近在看一些dbcp的相关内容,顺便做一下记录,免得自己给忘记了. 1. 引入dbcp (选择1.4) Java代码 ...
- 关于CRC循环冗余校验的总结(C#)
1. 实验要求 (1)通过CRC(循环冗余校对)序列的计算,掌握C#语言中类的静态方法与动态方法的区别. (2)Shell与Windows Form 的通信作为扩展提高内容. 2. 实验内容 主要工 ...
- jquery教程-Jquery 获取标签个数 size()函数用法
jquery教程-Jquery 获取标签个数 size()函数用法,size() 方法返回被 jQuery 选择器匹配的元素的数量. 语法 $(selector).size() jQuery ...
- hdu 1175 连连看 (深搜)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1175 题目大意:如果某两个相同的棋子,可以通过一条线连起来(这条线不能经过其它棋子)这样的两个棋子可以 ...
- <script>中的async与defer属性
1.script元素中的defer属性 1.1说明 使用该属性可以使脚本延迟到文档完全被解析和显示之后再按照原本的顺序执行,即告诉浏览器立即下载脚本,但延迟执行,该属性只对外部脚本有效 1.2使用方法 ...