题意:
给定正整数m以及n个01串s1~sn,你需要求出长度为2m的反对称的包含这n个01串作为子串的01串的个数。对998244353取模。
一个01串s是反对称的当且仅当它对于
1
<
=
i
<
=
∣
s
∣
1<=i<=|s|
1<=i<=∣s∣都满足
s
[
i
]
≠
s
[
∣
s
∣
−
i
+
1
]
s[i]≠s[|s|-i+1]
s[i]̸=s[∣s∣−i+1]。
题解:
先考虑不需要反回文怎么做
显然就是AC自动机+dp的经典操作,
f
[
i
]
[
x
]
[
s
]
f[i][x][s]
f[i][x][s]表示前
i
i
i位,到
x
x
x节点,当前子串出现状态为
s
s
s的方案数,
a
n
s
=
∑
x
=
1
t
o
t
f
[
m
]
[
x
]
[
2
n
−
1
]
ans=\sum_{x=1}^{tot}f[m][x][2^n-1]
ans=∑x=1totf[m][x][2n−1]
那么现在只dp前半部分,首先将每个串的逆反串加入自动机,但是还有一个问题,就是跨过左右两部分的子串比较麻烦。
可以发现,只有子串的某些位置,才会出现这种情况,那么只要找这些位置,和自动机上对应的节点,标记下即可,那么就有
a
n
s
=
∑
x
=
1
t
o
t
f
[
m
]
[
x
]
[
s
]
(
s
&
d
x
=
2
n
−
1
)
ans=\sum_{x=1}^{tot}f[m][x][s](s \& d_x=2^n-1)
ans=∑x=1totf[m][x][s](s&dx=2n−1)其中d为这个点可以制造的跨两边的串的集合。
code:
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=998244353;
struct trnode{
int a[2],fail,c,d;
}tr[1210];int tot=1;
int n,m,pre[2][110];
char s[8][110];
void insert(int k)
{
int len=strlen(s[k]+1),x=1;
for(int i=1;i<=len;i++)
{
int c=s[k][i]-'0';
if(!tr[x].a[c]) tr[x].a[c]=++tot;
x=tr[x].a[c];
}
tr[x].c|=(1<<k);
}
queue<int> q;
void make_fail()
{
q.push(1);
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=0;i<2;i++) if(tr[x].a[i])
{
int y=tr[x].a[i];
if(x==1) tr[y].fail=1;
else
{
int j=tr[x].fail;
while(!tr[j].a[i]) j=tr[j].fail;
tr[y].fail=tr[j].a[i];
tr[y].c|=tr[tr[j].a[i]].c;
tr[y].d|=tr[tr[j].a[i]].d;
}
q.push(y);
}
}
for(int x=1;x<=tot;x++)
for(int i=0;i<2;i++)
{
int j=x;
while(!tr[j].a[i]) j=tr[j].fail;
tr[x].a[i]=tr[j].a[i];
}
}
bool check(int k,int l,int n,int op)
{
if(l*2<n) return false;
if(l*2==n&&op) return false;
for(int i=1;l+i<=n;i++)
if(s[k][l-i+1]==s[k][l+i]) return false;
return true;
}
int f[510][1210][1<<6];
void solve(int k)
{
for(int x=1;x<=tot;x++)
for(int s=0;s<1<<n;s++) if(f[k][x][s])
{
for(int c=0;c<2;c++)
{
int y=tr[x].a[c],t=s|tr[y].c;
(f[k+1][y][t]+=f[k][x][s])%=mod;
}
}
}
int main()
{
tr[1].a[0]=++tot;tr[1].a[1]=++tot;
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++) scanf("%s",s[i]+1),insert(i);
for(int i=0;i<n;i++)
{
int len=strlen(s[i]+1),x=1;
for(int j=1;j<=len;j++)
{
x=tr[x].a[s[i][j]-'0'];
if(check(i,j,len,0)) tr[x].d|=(1<<i);
}
reverse(s[i]+1,s[i]+len+1);
for(int j=1;j<=len;j++) s[i][j]=(s[i][j]=='0')?'1':'0';
}
for(int i=0;i<n;i++) insert(i);
for(int i=0;i<n;i++)
{
int len=strlen(s[i]+1),x=1;
for(int j=1;j<=len;j++)
{
x=tr[x].a[s[i][j]-'0'];
if(check(i,j,len,1)) tr[x].d|=(1<<i);
}
}
make_fail();
f[0][1][0]=1;
for(int i=0;i<m;i++) {solve(i);}
int ans=0;
for(int x=1;x<=tot;x++)
for(int s=0;s<1<<n;s++)
if(f[m][x][s]) if((s|tr[x].d)==(1<<n)-1) (ans+=f[m][x][s])%=mod;
printf("%d",ans);
}