雅礼集训【Day6-1】字符串
雅礼集训【Day6-1】字符串

假设我们有串\(a\),我们设\(a'\)为\(a\)翻转后按为取反过后的串。
我们只考虑前一半的,长为\(m\)的串。如果前半截匹配了\(a\)或者\(a'\),则\(a\)就被匹配上了。所以我们记\(f_{i,j,S}\)表示长度\(i\),在AC自动机上匹配到了\(j\)节点,已经匹配了的串的集合为\(S\)的方案数。
但是可能会出现\(a\)出现的位置跨越了\(m\),这样我们就会出问题。因为我们记录了生成的串在AC自动机上匹配的节点,所以我们就能得到\(a\)在前半截中匹配的长度。如果这个长度\(\geq\lfloor \frac{len}{2}\rfloor\),则我们能知道后半截是什么,也就能判断是否合法了。
那如果\(a\)没有匹配长度\(\geq\lfloor \frac{len}{2}\rfloor\)的情况呢?我们发现,此时\(a'\)的匹配长度一定\(\geq\lfloor \frac{len}{2}\rfloor\),所以我们同时判断\(a\)和\(a'\),其中一个成立就行了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define N 2005
#define M 505
using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
const ll mod=998244353;
int n,m;
string str[20];
struct trie {
	int ch[2];
	int w;
	int fail;
	int flag;
	int len;
}tr[N];
int rt=1;
int cnt=1;
void Insert(string s,int id) {
	int len=s.length();
	int v=1;
	for(int i=0;i<len;i++) {
		int j=s[i]-'0';
		if(!tr[v].ch[j]) {
			tr[v].ch[j]=++cnt;
			tr[tr[v].ch[j]].len=tr[v].len+1;
		}
		v=tr[v].ch[j];
	}
	tr[v].w|=1<<id-1;
}
queue<int>q;
void build_fail() {
	for(int i=0;i<2;i++) {
		if(!tr[1].ch[i]) tr[1].ch[i]=1;
		else {
			int x=tr[1].ch[i];
			tr[x].fail=1;
			q.push(x);
		}
	}
	while(!q.empty()) {
		int v=q.front();
		q.pop();
		tr[v].w|=tr[tr[v].fail].w;
		for(int i=0;i<2;i++) {
			if(!tr[v].ch[i]) tr[v].ch[i]=tr[tr[v].fail].ch[i];
			else {
				int sn=tr[v].ch[i];
				int f=tr[v].fail;
				tr[sn].fail=tr[f].ch[i];
				q.push(sn);
			}
		}
	}
}
int pre[20][N];
int f[M][1205][1<<6];
bool pd(string &a,int len,int v,int id) {
	int now=1;
	tr[1].flag=1;
	for(int i=0;i<len;i++) {
		now=tr[now].ch[a[i]-'0'];
		tr[now].flag=1;
	}
	while(!tr[v].flag) v=tr[v].fail;
	int nlen=0;
	now=1;
	for(int i=0;i<len&&now!=v;i++,nlen++) {
		now=tr[now].ch[a[i]-'0'];
	}
	int flag=0;
	while(v) {
		if(tr[v].flag&&pre[id][tr[v].len]) flag=1;
		v=tr[v].fail;
	}
	tr[1].flag=0;
	now=1;
	for(int i=0;i<len;i++) {
		now=tr[now].ch[a[i]-'0'];
		tr[now].flag=0;
	}
	return flag;
}
bool chk(int v,int S) {
	for(int i=1;i<=n;i++) {
		if(S>>i-1&1) continue ;
		int len=str[i].length();
		if(!pd(str[i],len,v,i)&&!pd(str[i+n],len,v,i+n)) return 0;
	}
	return 1;
}
int main() {
	n=Get(),m=Get();
	for(int i=1;i<=n;i++) cin>>str[i];
	for(int i=1;i<=n;i++) {
		int len=str[i].length();
		for(int j=len-1;j>=0;j--) {
			str[i+n].push_back(str[i][j]^1);
		}
	}
	for(int i=1;i<=n;i++) {
		Insert(str[i],i);
		Insert(str[i+n],i);
	}
	build_fail();
	f[0][1][0]=1;
	for(int i=0;i<m;i++) {
		for(int j=1;j<=cnt;j++) {
			for(int S=0;S<1<<n;S++) {
				if(!f[i][j][S]) continue ;
				for(int k=0;k<2;k++) {
					int to=tr[j].ch[k];
					(f[i+1][to][S|tr[to].w]+=f[i][j][S])%=mod;
				}
			}
		}
	}
	for(int i=1;i<=2*n;i++) {
		int len=str[i].length();
		for(int st=1;st<=len;st++) {
			if(st*2<len) continue ;
			int flag=1;
			for(int j=st;j<len;j++) {
				if(str[i][j]==str[i][2*st-j-1]) flag=0;
			}
			pre[i][st]=flag;
		}
	}
	int ans=0;
	for(int i=1;i<=cnt;i++) {
		for(int S=0;S<1<<n;S++) {
			if(!f[m][i][S]) continue ;
			if(chk(i,S)) {
				(ans+=f[m][i][S])%=mod;
			}
		}
	}
	cout<<ans;
	return 0;
}
雅礼集训【Day6-1】字符串的更多相关文章
- 雅礼集训 Day6 T2 Equation 解题报告
		Equation 题目描述 有一棵\(n\)个点的以\(1\)为根的树,以及\(n\)个整数变量\(x_i\).树上\(i\)的父亲是\(f_i\),每条边\((i,f_i)\)有一个权值\(w_i\ ... 
- 雅礼集训 Day6 T1 Merchant 解题报告
		Merchant 题目描述 有\(n\)个物品,第\(i\)个物品有两个属性\(k_i,b_i\),表示它在时刻\(x\)的价值为\(k_i\times x+b_i\). 当前处于时刻\(0\),你可 ... 
- [LOJ 6031]「雅礼集训 2017 Day1」字符串
		[LOJ 6031] 「雅礼集训 2017 Day1」字符串 题意 给定一个长度为 \(n\) 的字符串 \(s\), \(m\) 对 \((l_i,r_i)\), 回答 \(q\) 个询问. 每个询 ... 
- 「雅礼集训 2017 Day7」事情的相似度
		「雅礼集训 2017 Day7」事情的相似度 题目链接 我们先将字符串建后缀自动机.然后对于两个前缀\([1,i]\),\([1,j]\),他们的最长公共后缀长度就是他们在\(fail\)树上对应节点 ... 
- 雅礼集训1-9day爆零记
		雅礼集训1-9day爆零记 先膜一下虐爆我的JEFF巨佬 Day0 我也不知道我要去干嘛,就不想搞文化科 (文化太辣鸡了.jpg) 听李总说可以去看(羡慕)各路大佬谈笑风声,我就报一个名吧,没想到还真 ... 
- LOJ_6045_「雅礼集训 2017 Day8」价 _最小割
		LOJ_6045_「雅礼集训 2017 Day8」价 _最小割 描述: 有$n$种减肥药,$n$种药材,每种减肥药有一些对应的药材和一个收益. 假设选择吃下$K$种减肥药,那么需要这$K$种减肥药包含 ... 
- 「雅礼集训 2017 Day2」解题报告
		「雅礼集训 2017 Day2」水箱 我怎么知道这种题目都能构造树形结构. 根据高度构造一棵树,在树上倍增找到最大的小于约束条件高度的隔板,开一个 \(vector\) 记录一下,然后对于每个 \(v ... 
- 「雅礼集训 2017 Day1」 解题报告
		「雅礼集训 2017 Day1」市场 挺神仙的一题.涉及区间加.区间除.区间最小值和区间和.虽然标算就是暴力,但是复杂度是有保证的. 我们知道如果线段树上的一个结点,\(max=min\) 或者 \( ... 
- [LOJ 6030]「雅礼集训 2017 Day1」矩阵
		[LOJ 6030] 「雅礼集训 2017 Day1」矩阵 题意 给定一个 \(n\times n\) 的 01 矩阵, 每次操作可以将一行转置后赋值给某一列, 问最少几次操作能让矩阵全为 1. 无解 ... 
随机推荐
- SET XACT_ABORT ON [SQL SERVER] 设置事务全部回滚
			SET XACT_ABORT ON 设置事务回滚的默认是OFF. 当为ON时,如果你存储中的某个地方出了问题,整个事务中的语句都会回滚为OFF时,只回滚错误的地方 
- mysql left join的深入探讨
			即使你认为自己已对 MySQL 的 LEFT JOIN 理解深刻,但我敢打赌,这篇文章肯定大致也许可能让你学会点东西! ON 子句与 WHERE 子句的不同 一种更好地理解带有 WHERE ... I ... 
- 1970年// iPhone “变砖”后可继续正常使用的解决方案
			0.解决方案 说话先说重点,“变砖”后的iphone怎么正常使用. 拆开后盖,给电源和处理器之间断下电就OK了. 1.事件来源 对于iPhone和iPad,把时间手动设置到1970年5月以前会出现“变 ... 
- 19 个常用的 JavaScript 简写方法
			来自:SangSir 链接:https://segmentfault.com/a/1190000012673854 原文:https://www.sitepoint.com/shorthand-jav ... 
- ViewPager结合view无限滑动
			使用viewPager进无限滑动,这里的实现是在适配器里面进行,当然在外头使用滑动监听也行. import android.support.v4.view.PagerAdapter; import a ... 
- Java synchronized解析
			多线程三大特性: 可见性.原子性.有序性 synchronize的特性: 1.同一时刻只有一个线程访问临界资源 2.其它未获取到锁执行权的线程必须排队等待 3.保证共享资源的原子性.可见性和有序性 4 ... 
- VysorPro助手
			Vysor是一款非常强大而又好用的Android远程显示及控制软件,有Chrome插件版.Windows客户端版和Mac版,是Android开发和测试人员的必备神器.其中Windows客户端版相对Ch ... 
- js的深层克隆和浅层克隆代码和理解
			<script> //判断是不是原始值 //判断是数组还是对象 //建立相应的数组或对象 var obj={ name:'辣鸡', sex:'male', card:['laobi','f ... 
- matlab练习程序(地图上画经纬度)
			需要看下生成的数据在地球上的经纬度具体位置. 投影为墨卡托投影. clear all; close all; clc; load coast; a=load('out.txt'); %自己的经纬度 ... 
- exports与module.exports的区别,export与export.defult区别
			在JS模块化编程中,之前使用的是require.js或者sea.js.随着前端工程化工具webpack的推出,使得前端js可以使用CommonJS模块标准或者使用ES6 moduel特性. 在Comm ... 
