2017 多校5 Rikka with String
2017 多校5 Rikka with String(ac自动机+dp)
题意:
Yuta has \(n\) \(01\) strings \(s_i\), and he wants to know the number of \(01\) antisymmetric strings of length \(2L\) which contain all given strings \(s_i\) as continuous substrings.
A \(01\) string \(s\) is antisymmetric if and only if \(s[i]≠s[|s|−i+1]\) for all \(i∈[1,|s|]\).
题解:
如果没有反对称串的限制,直接求一个长度为 \(L\) 的 \(01\) 串满足所有给定串都出现过,那么是一个经典的 \(AC\) 自动机的问题,状态 $f[i][j][S] $表示长度为 \(i\),目前在 \(AC\) 自动机的节点 \(j\) 上,已经出现的字符串集合为 \(S\) 的方案数,然后直接转移即可,时间复杂度\(O(2^{n}L\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|})\)。
之前做过一道ac自动机+dp题,状态定义简直一模一样,这道题一直卡在不知道怎么处理跨越中轴线的字符串,我是只建了一个自动机,把正串和翻转的反串都插在里面,然后从左开始往右走到离中间只剩20个字符,这样的做法到这里,我只能针对Trie图上的每一个结点爆枚\(O(2^{20})\)次,所以需要再开一个自动机只把正串插在里面,以供跨越中轴的来计算,开始也想过从中间往两边走,似乎不好处理就没有仔细想了。
看了题解真是恍然大悟啊,从中间往两边,这样就只需要做一次爆枚就可以把Trie图所有节点的初始值都处理出来了,然后接着dp就好了,题解的做法开四维数组爆内存了,只能把最后的dp写成递推形式,优化成滚动数组过去,好麻烦啊,看了其它人代码内存没这么高,也不知道是什么写法。
然后突然想到了另一种做法,不需要往两边dp,往一边就可以了,我从中轴的右边\(max|s|\)字符开始走,把正串和翻转的反串都插在这个自动机里,然后爆枚就是从中轴左边的\(max|s|-1\)个字符到右边的\(max|s|-1\)个字符,这样也只需要一次就可以预处理所有初始值,这样最后dp不用写递推,dfs就好了,好写多,也比题解要快一些。
贴一下两份代码
往两边dp
#include<bits/stdc++.h>
#define LL long long
#define P pair<int,int>
#define ls(i) seg[i].lc
#define rs(i) seg[i].rc
using namespace std;
const int mod = 998244353;
const int SIZE = 2;
const int MAXNODE = 130;
int read(){
int x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x;
}
int n,L;
char s[7][22];
int w[110];
int f[2][130][130][1<<6];
struct AC{
int ch[MAXNODE][SIZE];
int f[MAXNODE],last[MAXNODE],val[MAXNODE];
int sz;
void init(){
sz = 1;
memset(ch[0], 0, sizeof ch[0]);
}
int idx(char c){
return c - '0';
}
void _insert(char* s, int v){
int u = 0,len = strlen(s);
for(int i = 0;i < len;i++){
int c = idx(s[i]);
if(!ch[u][c]){
memset(ch[sz], 0, sizeof ch[sz]);
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] |= (1<<v);
}
void getFail(){
queue<int> q;
f[0] = 0;
for(int c = 0;c < SIZE;c++){
int u = ch[0][c];
if(u){
f[u] = 0;
q.push(u);
last[u] = 0;
}
}
while(!q.empty()){
int r = q.front();q.pop();
for(int c = 0;c < SIZE;c++){
int u = ch[r][c];
if(!u){
ch[r][c] = ch[f[r]][c];
continue;
}
q.push(u);
int v = f[r];
while(v && !ch[v][c]) v = f[v];
f[u] = ch[v][c];
last[u] = val[f[u]]?f[u]:last[f[u]];
val[u] |= val[last[u]];
}
}
}
}acL,acR;
int maxs,o[22];
void dfs(int i,int j,int len,int s){
s |= acL.val[i];
s |= acR.val[j];
if(len == maxs) {
int u = 0;
for(int i = len - 1;i >= 0;i--){
u = acR.ch[u][o[i]];
s |= acR.val[u];
}
for(int i = 0;i < len;i++){
u = acR.ch[u][!o[i]];
s |= acR.val[u];
}
f[0][i][j][s]++;
return ;
}
for(int c = 0;c < 2;c++) o[len] = c,dfs(acL.ch[i][c],acR.ch[j][!c],len + 1,s);
}
void add(int &x,int y){
x += y;
if(x >= mod) x -= mod;
}
int main(){
w[0] = 1;
for(int i = 1;i <= 100;i++) w[i] = 1LL * w[i-1] * 2 % mod;
int T = read();
while(T--){
n = read(),L = read();
acL.init();
acR.init();
maxs = 0;
for(int i = 0;i < n;i++) {
scanf("%s",s[i]);
int len = strlen(s[i]);
maxs = max(maxs,len);
acR._insert(s[i],i);
reverse(s[i],s[i] + len);
acL._insert(s[i],i);
}
maxs--;
maxs = min(maxs,L);
int total = (1<<n) - 1;
acL.getFail();
acR.getFail();
memset(f,0,sizeof(f));
dfs(0,0,0,0);
for(int l = 0;l < L - maxs;l++){
int o = l % 2, oo = !o;
memset(f[oo],0,sizeof(f[oo]));
for(int i = 0;i < acL.sz;i++){
for(int j = 0;j < acR.sz;j++) {
for(int s = 0;s <= total;s++){
if(f[o][i][j][s]){
for(int c = 0;c < 2;c++){
int ni = acL.ch[i][c],nj = acR.ch[j][!c];
add(f[oo][ni][nj][s | acL.val[ni] | acR.val[nj]],f[o][i][j][s]);
}
}
}
}
}
}
int ans = 0;
int o = (L - maxs) % 2;
for(int i = 0;i < acL.sz;i++)
for(int j = 0;j < acR.sz;j++) add(ans,f[o][i][j][total]);
printf("%d\n",ans);
}
return 0;
}
往右边走
#include<bits/stdc++.h>
#define LL long long
#define P pair<int,int>
#define ls(i) seg[i].lc
#define rs(i) seg[i].rc
using namespace std;
const int mod = 998244353;
const int SIZE = 2;
const int MAXNODE = 300;
const int MAXN = MAXNODE;
int read(){
int x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x;
}
int n,L;
char s[7][22];
int w[110];
int f[250][110][1<<6];
struct AC{
int ch[MAXNODE][SIZE];
int f[MAXNODE],last[MAXNODE],val[MAXNODE];
int sz;
void init(){
sz = 1;
memset(ch[0], 0, sizeof ch[0]);
}
int idx(char c){
return c - '0';
}
void _insert(char* s, int v){
int u = 0,len = strlen(s);
for(int i = 0;i < len;i++){
int c = idx(s[i]);
if(!ch[u][c]){
memset(ch[sz], 0, sizeof ch[sz]);
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] |= (1<<v);
}
void getFail(){
queue<int> q;
f[0] = 0;
for(int c = 0;c < SIZE;c++){
int u = ch[0][c];
if(u){
f[u] = 0;
q.push(u);
last[u] = 0;
}
}
while(!q.empty()){
int r = q.front();q.pop();
for(int c = 0;c < SIZE;c++){
int u = ch[r][c];
if(!u){
ch[r][c] = ch[f[r]][c];
continue;
}
q.push(u);
int v = f[r];
while(v && !ch[v][c]) v = f[v];
f[u] = ch[v][c];
last[u] = val[f[u]]?f[u]:last[f[u]];
val[u] |= val[last[u]];
}
}
}
}acL,acR;
int maxs,o[21],total,ans;
int dp(int i,int len,int s){
if(s == total) return w[L - len];
if(len == L) return s == total;
int &res = f[i][len][s];
if(res != -1) return res;
res = 0;
for(int c = 0;c < 2;c++) res = (res + dp(acR.ch[i][c],len + 1,(s | acR.val[acR.ch[i][c]])))%mod;
return res;
}
void dfs(int len){
if(len == maxs) {
int u = 0,v = 0,s = 0;
for(int i = len - 1;i >= 0;i--){
u = acL.ch[u][!o[i]];
v = acR.ch[v][!o[i]];
s |= acL.val[u];
}
for(int i = 0;i < len;i++){
u = acL.ch[u][o[i]];
v = acR.ch[v][o[i]];
s |= acL.val[u];
}
ans = (ans + dp(v,len,s))%mod;
return ;
}
for(int c = 0;c < 2;c++) o[len] = c,dfs(len + 1);
}
int main(){
w[0] = 1;
for(int i = 1;i <= 100;i++) w[i] = 1LL * w[i-1] * 2 % mod;
int T = read();
while(T--){
n = read(),L = read();
acL.init();
acR.init();
maxs = 0;
for(int i = 0;i < n;i++) {
scanf("%s",s[i]);
int len = strlen(s[i]);
maxs = max(maxs,len);
acR._insert(s[i],i);
acL._insert(s[i],i);
reverse(s[i],s[i] + len);
for(int j = 0;j < len;j++) if(s[i][j] == '1') s[i][j] = '0';else s[i][j] = '1';
acR._insert(s[i],i);
}
ans = 0;
maxs--;
maxs = min(L,maxs);
total = (1<<n) - 1;
acL.getFail();
acR.getFail();
memset(f,-1,sizeof(f));
dfs(0);
printf("%d\n",ans);
}
return 0;
}
2017 多校5 Rikka with String的更多相关文章
- 2017 多校5 hdu 6093 Rikka with Number
2017 多校5 Rikka with Number(数学 + 数位dp) 题意: 统计\([L,R]\)内 有多少数字 满足在某个\(d(d>=2)\)进制下是\(d\)的全排列的 \(1 & ...
- 2016暑假多校联合---Rikka with Sequence (线段树)
2016暑假多校联合---Rikka with Sequence (线段树) Problem Description As we know, Rikka is poor at math. Yuta i ...
- hdu.5202.Rikka with string(贪心)
Rikka with string Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others ...
- 【Hihocoder1413】Rikka with String(后缀自动机)
[Hihocoder1413]Rikka with String(后缀自动机) 题面 Hihocoder 给定一个小写字母串,回答分别把每个位置上的字符替换为'#'后的本质不同的子串数. 题解 首先横 ...
- HDU 6086 Rikka with String
Rikka with String http://acm.hdu.edu.cn/showproblem.php?pid=6086 题意: 求一个长度为2L的,包含所给定的n的串,并且满足非对称. 分析 ...
- 2017 多校4 Wavel Sequence
2017 多校4 Wavel Sequence 题意: Formally, he defines a sequence \(a_1,a_2,...,a_n\) as ''wavel'' if and ...
- 2017 多校4 Security Check
2017 多校4 Security Check 题意: 有\(A_i\)和\(B_i\)两个长度为\(n\)的队列过安检,当\(|A_i-B_j|>K\)的时候, \(A_i和B_j\)是可以同 ...
- 2017 多校3 hdu 6061 RXD and functions
2017 多校3 hdu 6061 RXD and functions(FFT) 题意: 给一个函数\(f(x)=\sum_{i=0}^{n}c_i \cdot x^{i}\) 求\(g(x) = f ...
- 2017 多校2 hdu 6053 TrickGCD
2017 多校2 hdu 6053 TrickGCD 题目: You are given an array \(A\) , and Zhu wants to know there are how ma ...
随机推荐
- BZOJ1491: [NOI2007]社交网络(Floyd 最短路计数)
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 2343 Solved: 1266[Submit][Status][Discuss] Descripti ...
- 网页更换主题以及绘制图形js代码实现
HTML代码实现: <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- yii2 的登录注册 轮子
//利用到了yii2 框架之中的验证规则 进行判定而已 也不是很高深的东西 但是 使用框架自身的轮子 会有安全性能的隐患 1注册reg controller 中 我都以admin 为例子 publi ...
- Jongmah CodeForces - 1110D
传送门 题意:你有n个数字,范围[1, m],你可以选择其中的三个数字构成一个三元组,但是这三个数字必须是连续的或者相同的,每个数字只能用一次,问这n个数字最多构成多少个三元组? 题解:三个一模一样的 ...
- 不同级域名中的 Cookie 共享
HTTP 响应头中 Set-Cookie 行未指定 domain 时则设置访问的域名 seliote.com 可以设置 seliote.com(也可以写成 .seliote.com 意思一样) 与 w ...
- Android面试收集录10 LruCache原理解析
一.Android中的缓存策略 一般来说,缓存策略主要包含缓存的添加.获取和删除这三类操作.如何添加和获取缓存这个比较好理解,那么为什么还要删除缓存呢?这是因为不管是内存缓存还是硬盘缓存,它们的缓存大 ...
- 二、mysql数据库之基本操作和存储引擎
一.知识储备 数据库服务器:一台计算机(对内存要求比较高) 数据库管理系统:如mysql,是一个软件 数据库:oldboy_stu,相当于文件夹 表:student,scholl,class_list ...
- Android 微信支付步骤
以前自己写过微信支付,今天让我用h5调用微信支付,结果还浪费了点时间,虽然时间不长. 1.导入jar包 .微信提供的jar包 2.写你的微信支付的代码,其实如果参数都有了就一段代码: public v ...
- 使用阿里开源工具 TProfiler 在海量业务代码中精确定位性能代码 (jvm性能调优)
技术交流群:233513714 本文是<JVM 性能调优实战之:一次系统性能瓶颈的寻找过程> 的后续篇,该篇介绍了如何使用 JDK 自身提供的工具进行 JVM 调优将 TPS 由 2.5 ...
- PowerShell技巧:使用XPath语法查询XML文件
[TechTarget中国原创] XML是存储结构化数据的一个很好的途径,但是想要让数据在其中发挥作用又会有些困难.每一种语言都有其特定方式来查询XML文件中的命名空间.元素及属性.PowerShel ...