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 自动机)的更多相关文章

  1. 【AC自动机】【状压dp】【滚动数组】hdu6086 Rikka with String

    给你m个01串,问你有多少个长度为2L的01串,满足前半段倒置取反后等于后半段,并且包含所有的m个01串. 考虑单词完全在中线前面或者后面的情况,直接将单词及其倒置取反插入AC自动机,AC自动机每个结 ...

  2. 基于trie树做一个ac自动机

    基于trie树做一个ac自动机 #!/usr/bin/python # -*- coding: utf-8 -*- class Node: def __init__(self): self.value ...

  3. AC自动机-算法详解

    What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...

  4. python爬虫学习(11) —— 也写个AC自动机

    0. 写在前面 本文记录了一个AC自动机的诞生! 之前看过有人用C++写过AC自动机,也有用C#写的,还有一个用nodejs写的.. C# 逆袭--自制日刷千题的AC自动机攻克HDU OJ HDU 自 ...

  5. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Sta ...

  6. BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3198  Solved: 1532[Submit][Status ...

  7. BZOJ 1212: [HNOI2004]L语言 [AC自动机 DP]

    1212: [HNOI2004]L语言 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1367  Solved: 598[Submit][Status ...

  8. [AC自动机]【学习笔记】

    Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)To ...

  9. AC自动机 HDU 3065

    大概就是裸的AC自动机了 #include<stdio.h> #include<algorithm> #include<string.h> #include< ...

随机推荐

  1. P2168 [NOI2015]荷马史诗

    题目描述 追逐影子的人,自己就是影子 ——荷马 Allison 最近迷上了文学.她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的<荷马史诗>.但是由<奥德赛&g ...

  2. [洛谷P2210]Haywire

    题目大意:有$n(n\leqslant12)$个数,每个数和其他三个数连边,求一个排列,使得边的长度最小 题解:状压$DP$,$f_{i,j}$表示当前确定的数状态为$i$,有$j$条边起点被确定终点 ...

  3. 对比append插入数据产生的redo量

    --版本信息 SELECT * FROM v$version; Oracle - Prod PL - Production CORE Production TNS - Production NLSRT ...

  4. 【转载】字符串最小表示法-O(n)算法

    原博客链接:http://blog.csdn.net/zy691357966/article/details/39854359 未授权,侵权删. 因为这篇博客写得真好..转载了.. 红色的字是原博主写 ...

  5. Linux : 多线程下载工具: axel

    wget 应该是最常用的下载工具了,但是其不支持多线程下载. axel 安装 epel 源有 axel 的二进制包,可以使用 yum 安装. yum install epel-release yum ...

  6. bzoj 2115 路径xor和最大值

    首先我们可以将这张图转化为一棵树,其中可能有好多环,肯定是从1走到N,其中可能经过好多环, 因为我们不走N在的一支,最后还是会走回来,因为a xor b xor b=a,所以相当于没走,就是走了个环, ...

  7. CentOS 7 主机加固手册-下

      CentOS 7 主机加固手册-上 CentOS 7 主机加固手册-中 CentOS 7 主机加固手册-下 0x1f 删除禁用非必要的服务 删除非必要的服务 # Remove yum remove ...

  8. [转]在树莓派上搭建LAMP服务

    之前介绍过树莓派上LNMP环境的搭建方法,本文将详细介绍如何在树莓派上配置LAMP服务. 为LAMP,是最流行的服务器配置之一,LAMP的含义是: Linux - 操作系统 Apache - 网络服务 ...

  9. python3,判断,循环练习1

    1.使用while循环输出1 2 3 4 5 6 8 9 10 i = 1 while i <= 10: if i == 7: i += 1 print(end=' ') continue pr ...

  10. 转:SWT中的Display 对象和 Shell对象

    转自: http://blog.csdn.net/chulaixi/article/details/3095478 我们书写swt程序的步骤,这些步骤是: 1. 创建一个Display对象 2. 创建 ...