题目描述
给定一个正整数mmm以及nnn个010101串s1,s2,…,sns_1,s_2,\dots,s_ns1,s2,…,sn,你需要求出长度为2m2m2m的反对称的包含这nnn个010101串作为子串的010101串的个数,对998244353998244353998244353取模。
一个010101串sss是反对称的当且仅当它对于1≤i≤∣s∣1\leq i\leq |s|1≤i≤∣s∣,都满足s[i]≠s[∣s∣−i+1]s[i]\neq s[|s|-i+1]s[i]=s[∣s∣−i+1]。
输入格式
第一行两个整数n,mn,mn,m,接下来nnn行每行一个字符串s1,s2,…,sns_1,s_2,\dots,s_ns1,s2,…,sn。
输出格式
一行一个整数表示答案模998244353998244353998244353后的值。
样例输入
2 3
011
001
样例输出
4
数据范围
1≤n≤6,1≤∣si∣≤100,1≤m≤5001\leq n\leq 6,1\leq |s_i|\leq100,1\leq m\leq 5001≤n≤6,1≤∣si∣≤100,1≤m≤500
题解
每个串有四种情况:在前一半出现,在后一半出现,跨越中间且前一半长度大,跨越中间且后一半长度大。
sss在后一半出现,也就是sss前后翻转,每位取反后的字符串在前一半出现,那么我们对每个串,都再记录一个翻转再取反后的串即可。
对于第三种情况,我们需要对字符串的所有超过一半的前缀判断如果以这个前缀为前半段的结尾,能否构造出这个字符串。对于第四种情况,将sss翻转再取反的操作按上述方法处理即可。
在ACACAC自动机上做状压DP即可。可以根据代码帮助理解。
时间复杂度为O(2n×n×∣si∣×m)O(2^n\times n\times |s_i|\times m)O(2n×n×∣si∣×m)。
注意
- 空间压得比较死,所以数组要开小一点,因为指涉及加法,所以不需要开long long
code
#include<bits/stdc++.h>
using namespace std;
const int N=1200;
int n,m,l,tot=0,w[N+5],fn[N+5],t[N+5],fa[N+5],ch[N+5][2];
//w表示这个点正串的结尾,fn表示这个点是反串的结尾,t表示fail指针
int ans=0,f[505][N+5][70];
int mod=998244353;
char s[505];
queue<int>g;
bool pd(int t){
for(int i=1;t+i<l;i++){
if(t-i+1<0||s[t-i+1]==s[i+t]) return 0;
}
return 1;
}
void pt(int k){
int q=0;
for(int i=0;i<l;i++){
if(!ch[q][s[i]-'0']){
ch[q][s[i]-'0']=++tot;
fa[tot]=q;
}
q=ch[q][s[i]-'0'];
if(pd(i)) fn[q]|=k;
}
w[q]|=k;
}
int gt(int u){
return ch[fa[u]][1]==u;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",s);
l=strlen(s);
pt(1<<i-1);
for(int j=0;j<l-j-1;j++) swap(s[j],s[l-j-1]);
for(int j=0;j<l;j++) s[j]^=1;
pt(1<<i-1);
}
g.push(0);
while(!g.empty()){
int u=g.front();g.pop();
if(ch[u][0]) g.push(ch[u][0]);
if(ch[u][1]) g.push(ch[u][1]);
if(!u) continue;
int v=t[fa[u]],q=gt(u);
while(v&&!ch[v][q]) v=t[v];
if(ch[v][q]&&ch[v][q]!=u) t[u]=ch[v][q];
if(!ch[u][0]) ch[u][0]=ch[t[u]][0];
if(!ch[u][1]) ch[u][1]=ch[t[u]][1];
w[u]|=w[t[u]];
fn[u]|=fn[t[u]];
}
f[0][0][0]=1;
for(int i=1;i<=m;i++){
for(int j=0;j<=tot;j++){
for(int p=0;p<(1<<n);p++){
int u=ch[j][0];
f[i][u][p|w[u]]=(f[i][u][p|w[u]]+f[i-1][j][p])%mod;
u=ch[j][1];
f[i][u][p|w[u]]=(f[i][u][p|w[u]]+f[i-1][j][p])%mod;
}
}
}
for(int i=0;i<=tot;i++){
for(int p=0;p<(1<<n);p++){
if((p|fn[i])==(1<<n)-1){
ans=(ans+f[m][i][p])%mod;
}
}
}
printf("%d",ans);
return 0;
}
该文章描述了一个关于字符串处理的问题,要求找到长度为2^m的反对称01串中,包含给定n个01串作为子串的个数。解决方案包括四种情况分析,使用AC自动机进行动态规划,时间复杂度为O(2^n*n*|s_i|*m),其中s_i为每个输入字符串。最后给出了C++代码实现。
406

被折叠的 条评论
为什么被折叠?



