题目大意:有个人想破解他邻居的密码,他邻居告诉了一些关于这个密码的信息,并且给他一个单词集合,他用这些信息判断一下最少有多少种密码。
1->, 所有的密码都是有小写字母组成。
2->,密码的长度是 n (1<= n <=25)。
3->,密码至少包含 k 种字符集里面的单词。
比如,给集合{"she", "he"},单词长度是3,最少包含两个单词的密码,很明显只能是“she”(题目表述的不清楚)。
分析:因为要统计记录到达每个点时候经过多少种不同的单词,所以需要用一种方法来保存这些信息,开一个数组来判重貌似是个很容易想到的办法,不过考虑时间复杂度的问题,建议还是不要这么干,因为字符集合的数量很少,只有10,所以我们可以使用状态压缩来保存这些信息,而且转移状态时候也很方便操作,不过有点需要注意,一定要把遍历子节点放在最内层循环,这样再遍历每一种状态的时候发先有0的情况可以continue一下,否则超时超的哇哇的.......
代码如下:
==========================================================================
#include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> #include<queue> using namespace std; const int MAXN = 107; const int MaxSon = 26; const int Mod = 20090717; const int oo = 1e9+7; struct Ac_Trie { int next[MAXN][MaxSon]; int Fail[MAXN], End[MAXN]; int cnt, root; int newnode() { for(int i=0; i<MaxSon; i++) next[cnt][i] = -1; Fail[cnt] = End[cnt] = false; return cnt++; } void InIt() { cnt = 0; root = newnode(); } void Insert(char s[], int t) { int now = root; for(int i=0; s[i]; i++) { int k = s[i]-'a'; if(next[now][k] == -1) next[now][k] = newnode(); now = next[now][k]; } End[now] = 1<<t; } void GetFial() { queue<int>Q; int now = root; for(int i=0; i<MaxSon; i++) { if(next[now][i] == -1) next[now][i] = root; else { Fail[next[now][i]] = root; Q.push(next[now][i]); } } while(Q.size()) { now = Q.front(); Q.pop(); for(int i=0; i<MaxSon; i++) { if(next[now][i] == -1) next[now][i] = next[Fail[now]][i]; else { Fail[next[now][i]] = next[Fail[now]][i]; Q.push(next[now][i]); } } End[now] |= End[Fail[now]]; } } }; Ac_Trie ac; int Find(int i) { int k=0; while(i) { if(i % 2) k++; i /= 2; } return k; } int main() { int N, M, K; int sum[1024] ={0}; for(int i=0; i<1024; i++) sum[i] = Find(i); while(scanf("%d%d%d", &N, &M, &K), N+M+K) { char s[MAXN]; ac.InIt(); for(int i=0; i<M; i++) { scanf("%s", s); ac.Insert(s, i); } ac.GetFial(); int dp[2][MAXN][1024] = {0}, op=0, Len = 1<<M; dp[1][0][0] = 1; while(N--) { memset(dp[op], 0, sizeof(dp[op])); for(int i=0; i<ac.cnt; i++) for(int k=0; k<Len; k++) {///把k放中间优化一下...否则超时 if(dp[op^1][i][k] == 0) continue; for(int j=0; j<MaxSon; j++) { (dp[op][ac.next[i][j]][k|ac.End[i]] += dp[op^1][i][k])%=Mod; } } op ^= 1; } int ans = 0; for(int i=0; i<ac.cnt; i++) for(int j=0; j<Len; j++) { if(sum[j] >= K || sum[ac.End[i]|j] >= K) { ans += dp[op^1][i][j]; ans %= Mod; } } printf("%d ", ans); } return 0; }