數據範圍:\(n<=6,|s_i|<=100,m<=500\)node
場上不會在ac自動機上面跑dp的我大概失去了智商==果真字符串這塊仍是有點薄弱啊ios
首先想一個比較好的計算方式:比較明白的一點是若是前\(m\)位肯定了,後\(m\)位天然也就肯定了,咱們將知足條件的串分紅三大類:c++
(1)匹配串都在前\(m\)位(前半段)ui
(2)匹配串都在後\(m\)位(後半段)spa
(3)匹配串跨\(m\)這個位置debug
第三類又能夠再分兩類:跨\(m\)這個位置的串在前半段的長度比較大、在後半段的長度比較大code
首先考慮前兩類怎麼計算:其實只要把正串和翻轉以後再\(01\)反轉的串都丟到ac自動機裏面而後跑dp就行了blog
看到這個\(n\)這麼小,大概差很少就是用來狀壓的了吧,因而粗暴地令\(f[i][j][k]\)表示肯定了前\(i\)位,當前在\(j\)這個節點,當前已經包含的串狀態爲\(k\),而後直接\(O(m*\)自動機節點數\(*2^n)\)暴力dp就行了ip
具體一點就是對於每一個節點記錄一個\(st[x]\)表示走到這個節點意味着包含了哪些字符串,預處理的時候從fail樹上面從上往下傳就行了(固然實現的時候並不用真的建出來,記錄一下bfs序而後直接傳就行了)字符串
最後就是第三種狀況,這個其實也比較好搞,對於每一個自動機上的節點咱們維護一個\(midst[x]\)表示這個節點做爲新串中的第\(m\)位能夠包含到哪些匹配串,咱們枚舉每一個匹配串(包括反串)的每一位,若是這個位置能夠做爲知足條件的串的第\(m\)位的話(說白了就是可從這個位置切開知足反對稱),而且在這裏切開以後知足前半段的長度更長的話(由於枚舉的字符串中既有正串也有反串,因此只要保證一種狀況就能夠將(3)中的兩小類不重不漏地算進去了),咱們將其加入對應的自動機節點的\(midst[x]\)裏面去,而後同理這個\(midst\)也要下傳,方式和上面的\(st\)同樣
最後查答案的時候枚舉\(f\)的後兩維,若是說當前的狀態\(k|midst[j]=\)滿狀態的話,就將\(f[m][j][k]\)加入答案中
代碼大概長這個樣子
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int N=15,L=110,M=510,MOD=998244353; char s[N][L]; int n,m,ans,all; int St(int x){if (x>n) x-=n; return 1<<x-1;} bool in(int st,int x){return st>>x-1&1;} int mul(int x,int y){return 1LL*x*y%MOD;} int plu(int x,int y){return 1LL*x+y-(1LL*x+y>=MOD?MOD:0);} namespace Ac{/*{{{*/ const int N=1210,C=2,ST=(1<<6)+10; queue<int> q; int ch[N][C],fail[N],st[N],lis[N],midst[N]; int f[M][N][ST]; int tot,rt; void init(){tot=0; rt=0;} void debug(){ for (int i=rt;i<=tot;++i) printf("%d ",st[i]); printf("\n"); } int newnode(){ fail[++tot]=0; st[tot]=0; for (int i=0;i<C;++i) ch[tot][i]=0; return tot; } void insert(int id){ int now=rt,c,len=strlen(s[id]); for (int i=0;i<len;++i){ c=s[id][i]-'0'; if (!ch[now][c]) ch[now][c]=newnode(); now=ch[now][c]; } st[now]|=St(id); } void build(){ int u,v; while (!q.empty()) q.pop(); q.push(rt); lis[0]=0; while (!q.empty()){ v=q.front(); q.pop(); lis[++lis[0]]=v; for (int i=0;i<C;++i){ if (!ch[v][i]){ ch[v][i]=ch[fail[v]][i]; continue; } if (v==rt) fail[ch[v][i]]=rt; else fail[ch[v][i]]=ch[fail[v]][i]; q.push(ch[v][i]); } } for (int i=1;i<=lis[0];++i) st[lis[i]]|=st[fail[lis[i]]]; } void dp(){ int u; f[0][rt][0]=1; for (int i=0;i<m;++i){ for (int j=rt;j<=tot;++j) for (int stt=0;stt<=all;++stt){ if (f[i][j][stt]==0) continue; for (int k=0;k<C;++k){ u=ch[j][k]; f[i+1][u][stt|st[u]]=plu(f[i+1][u][stt|st[u]],f[i][j][stt]); } } } } bool check(int which,int mid){ int tot1=mid,tot2=mid+1,len=strlen(s[which]); while (tot1>=0&&tot2<len){ if (s[which][tot1]==s[which][tot2]) return 0; --tot1; ++tot2; } return 1; } void calc_mid(){ int len,now,c; for (int i=1;i<=n*2;++i){ len=strlen(s[i]); now=rt; for (int j=0;j<len-1;++j){ c=s[i][j]-'0'; if (check(i,j)&&(j+1)*2>=len) midst[ch[now][c]]|=St(i); now=ch[now][c]; } } for (int i=1;i<=lis[0];++i) midst[lis[i]]|=midst[fail[lis[i]]]; } void solve(){ build(); dp(); calc_mid(); ans=0; for (int i=rt;i<=tot;++i){ for (int stt=0;stt<=all;++stt){ if ((midst[i]|stt)==all) ans=plu(ans,f[m][i][stt]); } } printf("%d\n",ans); } }/*}}}*/ int main(){ #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); #endif scanf("%d%d",&n,&m); int len; Ac::init(); all=1<<n; --all; for (int i=1;i<=n;++i){ scanf("%s",s[i]); len=strlen(s[i]); for (int j=0;j<len;++j) s[n+i][len-1-j]='0'+((s[i][j]-'0')^1); Ac::insert(i); Ac::insert(n+i); } Ac::solve(); }