【AC自动机】【状压dp】【滚动数组】hdu6086 Rikka with String
给你m个01串,问你有多少个长度为2L的01串,满足前半段倒置取反后等于后半段,并且包含所有的m个01串。
考虑单词完全在中线前面或者后面的情况,直接将单词及其倒置取反插入AC自动机,AC自动机每个结点用个tag压位记录单词集合。
对于跨越中线的情况,比如说110010是一个单词,枚举一个中线,
11 | 0010
在前面添加上10,变成10 11 | 0010,
那么其前半部分1011也是一个合法的单词,只不过这种类型的单词只有在dp到长度恰好为L时才能用。
于是AC自动机上每个结点记录两种tag,tag1是原始的单词集合(还有将它倒置再取反的单词),tag2是跨越中线的单词。
f(i,j,S)表示长度为i,到第j个结点,当前集合为S的方案数。第一维滚动。i枚举到n-1时,要用tag2,否则只用tag1。
#include<cstdio> #include<queue> #include<string> #include<iostream> #include<algorithm> #include<cstring> using namespace std; queue<int>q; #define MOD 998244353 int child[2645][2],fail[2645],size,f[2][2645][64],tag[2645],tag2[2645]; void Insert(string S,int id) { int len=S.length(); int now=0; for(int i=0;i<len;++i) { if(!child[now][S[i]-'0']) child[now][S[i]-'0']=size++; now=child[now][S[i]-'0']; } tag[now]|=(1<<id); } void Inser2(string S,int id){ int len=S.length(); int now=0; for(int i=0;i<len;++i) { if(!child[now][S[i]-'0']) child[now][S[i]-'0']=size++; now=child[now][S[i]-'0']; } tag2[now]|=(1<<id); } void build() { fail[0]=-1; q.push(0); while(!q.empty()) { int U=q.front(); q.pop(); for(int i=0;i<2;++i) if(child[U][i]) { int V=fail[U]; while(V!=-1) { if(child[V][i]) { fail[child[U][i]]=child[V][i]; break; } V=fail[V]; } if(V==-1) fail[child[U][i]]=0; tag[child[U][i]]|=tag[fail[child[U][i]]]; tag2[child[U][i]]|=tag2[fail[child[U][i]]]; q.push(child[U][i]); } else if(U) child[U][i]=child[fail[U]][i]; } } void Init() { memset(child,0,sizeof(child)); memset(fail,0,sizeof(fail)); memset(tag,0,sizeof(tag)); memset(tag2,0,sizeof(tag2)); size=1; } int n,m,T; int main() { // freopen("b.in","r",stdin); scanf("%d",&T); string s; for(;T;--T){ scanf("%d%d",&m,&n); Init(); for(int i=0;i<m;++i){ cin>>s; int len=s.length(); // cout<<"::"<<s<<endl; Insert(s,i); string t=s; reverse(t.begin(),t.end()); for(int j=0;j<len;++j){ t[j]=((t[j]-'0')^1)+'0'; } // cout<<"::"<<t<<endl; Insert(t,i); for(int j=0;j<min(len,n);++j){ bool flag=1; for(int k=j+1,l=j;k<s.length() && l>=0;++k,--l){ if((s[k]-'0')^(s[l]-'0')!=1){ flag=0; break; } } if(!flag){ continue; } t=s.substr(0,j+1); for(int k=(j+1)*2;k<s.length();++k){ t=(char)(((s[k]-'0')^1)+'0')+t; } // cout<<":"<<t<<endl; Inser2(t,i); } } build(); memset(f,0,sizeof(f)); f[0][0][0]=1; bool cur=0; for(int i=0;i<n;++i){ memset(f[cur^1],0,sizeof(f[cur^1])); for(int j=0;j<size;++j) for(int k=0;k<(1<<m);++k){ if(!f[cur][j][k]) continue; if(i<n-1) for(int l=0;l<2;++l) f[cur^1][child[j][l]][k|tag[child[j][l]]]= (f[cur^1][child[j][l]][k|tag[child[j][l]]]+f[cur][j][k])%MOD; else{ for(int l=0;l<2;++l) f[cur^1][child[j][l]][k|tag[child[j][l]]|tag2[child[j][l]]]= (f[cur^1][child[j][l]][k|tag[child[j][l]]|tag2[child[j][l]]]+f[cur][j][k])%MOD; } } cur^=1; } int ans=0; for(int j=0;j<size;++j) ans=(ans+f[cur][j][(1<<m)-1])%MOD; printf("%d\n",ans); } return 0; }
——The Solution By AutSky_JadeK From UESTC
转载请注明出处:http://www.cnblogs.com/autsky-jadek/