Yuta has n01 strings si, and he wants to know the number of 01 antisymmetric strings of length 2L which contain all given strings si as continuous substrings.
A 01 string s is antisymmetric if and only if s[i]≠s[|s|−i+1] for all i∈[1,|s|].
It is too difficult for Rikka. Can you help her?
In the second sample, the strings which satisfy all the restrictions are 000111,001011,011001,100110
For each testcase, the first line contains two numbers n,L(1≤n≤6,1≤L≤100).
Then n lines follow, each line contains a 01 string si(1≤|si|≤20). Output For each testcase, print a single line with a single number -- the answer modulo 998244353.Sample Input
2 2 2 011 001 2 3 011 001Sample Output
1
分析:現在要求的是包含所有模板的情況,因爲n很小所以我們可以容斥做,每次枚舉一個子集然後求不包含這個子集的任何一個模板的情況,把模板全部翻轉後再插入就可以
表示後綴了,dp[i][j][k]表示當前匹配到第i位其中前綴匹配到自動機的第j個點,後綴匹配到第k個點的方案數,最後枚舉一下dp[L][j][k]中合法的j,k再統計答案就可了,這一步可以
暴力沿着fail指針遍歷判斷前綴後綴會不會拼出新的模板.
#include<bits/stdc++.h>
using namespace std;
const int MOD = 998244353;
const int MAXNODE = 125;
int T,n,l,ans,rc[1<<6],dp[105][125][125];
char s[7][25],rs[7][25];
struct ACautomata
{
int ch[MAXNODE][2];
int num[MAXNODE];
int deep[MAXNODE];
int f[MAXNODE]; // fail函數
int val[MAXNODE]; // 是否爲單詞結尾
int tot; // trie 單詞總數
int last[MAXNODE];
void init()
{
tot = 1;
memset(ch,0,sizeof(ch));
memset(num,0,sizeof(num));
memset(last,0,sizeof(last));
memset(f,0,sizeof(f));
}
int idx(char c)
{
return c - '0';
}
void insert(char *s,int v)
{
int u = 0,n = strlen(s);
for(int i = 0;i < n;i++)
{
int c = idx(s[i]);
if(!ch[u][c])
{
val[tot] = 0;
deep[tot] = deep[u] + 1;
ch[u][c] = tot++;
}
num[u] |= 1<<(v-1);
u = ch[u][c];
}
val[u] = true;
num[u] |= 1<<(v-1);
}
void getFail()
{
queue<int> q;
f[0] = 0;
for(int c = 0;c < 2;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 < 2;c++)
{
int u = ch[r][c];
if(!u)
{
ch[r][c] = ch[f[r]][c];
continue;
}
q.push(u);
f[u] = ch[f[r]][c];
last[u] = val[f[u]] ? f[u] : last[f[u]];
}
}
}
}tree1,tree2;
bool check(int x,int y)
{
if(tree1.val[x] || tree2.val[y] || tree1.last[x] || tree2.last[y]) return false;
for(int i = 1;i <= n;i++)
{
int len = strlen(s[i]);
for(int u = x;u;u = tree1.f[u])
if(tree1.num[u] & (1<<(i-1)))
for(int v = y;v;v = tree2.f[v])
if(tree2.num[v] & (1<<(i-1)))
{
if(tree1.deep[u] + tree2.deep[v] < len) break;
if(tree1.deep[u] + tree2.deep[v] == len) return false;
}
}
return true;
}
int main()
{
cin.sync_with_stdio(false);
cin>>T;
while(T--)
{
ans = 0;
cin>>n>>l;
for(int i = 1;i <= n;i++)
{
cin>>s[i];
int m = strlen(s[i]);
for(int j = 0;j < m;j++) rs[i][j] = s[i][m-1-j];
rs[i][m] = s[i][m];
}
rc[0] = 1;
for(int i = 1;i < (1<<n);i++) rc[i] = rc[i - (i & -i)] * -1;
for(int mask = 0;mask < (1<<n);mask++)
{
tree1.init(),tree2.init();
memset(dp,0,sizeof(dp));
for(int i = 1;i <= n;i++)
if(mask & (1<<(i-1))) tree1.insert(s[i],i),tree2.insert(rs[i],i);
tree1.getFail();
tree2.getFail();
dp[0][0][0] = 1;
for(int i = 0;i < l;i++)
for(int j = 0;j < tree1.tot;j++)
for(int k = 0;k < tree2.tot;k++)
if(dp[i][j][k] && !tree1.val[j] && !tree2.val[k] && !tree1.last[j] && !tree2.last[k])
for(int c = 0;c < 2;c++)
{
int next1 = tree1.ch[j][c],next2 = tree2.ch[k][c^1];
dp[i+1][next1][next2] += dp[i][j][k];
dp[i+1][next1][next2] %= MOD;
}
for(int j = 0;j < tree1.tot;j++)
for(int k = 0;k < tree2.tot;k++)
if(dp[l][j][k] && check(j,k))
{
ans += (MOD + dp[l][j][k]*rc[mask] % MOD) % MOD,ans %= MOD;
if(ans < 0) cout<<233<<endl;
}
}
cout<<ans<<endl;
}
}