HDU-2825 Wireless Password(AC自动机+状压DP)
题目大意:给一系列字符串,用小写字母构造出长度为n的至少包含k个字符串的字符串,求能构造出的个数。
题目分析:在AC自动机上走n步,至少经过k个单词节点,求有多少种走法。
代码如下:
# include<iostream> # include<cstdio> # include<queue> # include<cstring> # include<algorithm> using namespace std; typedef long long LL; const int mod=20090717; int ch[105][26]; int cnt,fail[105]; int val[105]; void init() { cnt=0; memset(ch,-1,sizeof(ch)); memset(val,0,sizeof(val)); } int idx(char x) { return x-'a'; } void insert(char *s,int k) { int len=strlen(s); int r=0; for(int i=0;i<len;++i){ int c=idx(s[i]); if(ch[r][c]==-1) ch[r][c]=++cnt; r=ch[r][c]; } val[r]|=(1<<k); } void getFail() { queue<int>q; fail[0]=0; for(int i=0;i<26;++i){ if(ch[0][i]==-1) ch[0][i]=0; else{ fail[ch[0][i]]=0; q.push(ch[0][i]); } } while(!q.empty()) { int u=q.front(); q.pop(); val[u]|=val[fail[u]]; for(int i=0;i<26;++i){ if(ch[u][i]==-1) ch[u][i]=ch[fail[u]][i]; else{ fail[ch[u][i]]=ch[fail[u]][i]; q.push(ch[u][i]); } } } } char word[15]; int dp[2][105][1<<10]; int getDigt(int x,int n) { int count=0; for(int i=0;i<n;++i) if(x&(1<<i)) ++count; return count; } int DP(int n,int m,int k) { memset(dp,0,sizeof(dp)); int cur=1; dp[0][0][0]=1; for(int i=0;i<n;++i){ memset(dp[cur],0,sizeof(dp[cur])); for(int j=0;j<=cnt;++j){ for(int s=0;s<(1<<m);++s) if(dp[cur^1][j][s]){ ///不加if语句会超时 for(int c=0;c<26;++c){ int &nxt=dp[cur][ch[j][c]][s|val[ch[j][c]]]; nxt=(nxt+dp[cur^1][j][s])%mod; } } } cur^=1; } int ans=0; for(int i=0;i<(1<<m);++i) if(getDigt(i,m)>=k){ for(int j=0;j<=cnt;++j) ans=(ans+dp[cur^1][j][i])%mod; } return ans; } int main() { int n,m,k; while(scanf("%d%d%d",&n,&m,&k)&&(n||m||k)) { init(); for(int i=0;i<m;++i){ scanf("%s",word); insert(word,i); } getFail(); printf("%d\n",DP(n,m,k)); } return 0; }