As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them:
Yuta has n 01 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
Input
The first line contains a number
t(1≤t≤5)
, the number of the testcases.
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
Sample Output
Yuta has n 01 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)
2 2 2 011 001 2 3 011 001
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; } }