HDU 2825 Wireless Password (AC自动机+DP+状态压缩)
2015-10-15 21:19
447 查看
题目大意
有m个单词,每个单词的长度不超过10。求长度为n的串包含至少k个单词的串的数量,所有字符均有小写字母组成。(0<=m<=10,1<=n<=25)分析
将所有单词构造成AC自动机用状态压缩表示单词,val[i]表示结点i包含的单词;
状态dp[i][j][k]表示长度为i的串,到达状态j,包含k的word时的数量
状态转移方程
dp[i+1][v][k|val[v]] += dp[i][j][k],其中v是j的后继状态
初始化dp[0][0][0] = 1,其余都是0 。ans = sum(dp
[j][i]),其中i >= k
代码
#include <iostream> #include <string> #include <cstring> #include <queue> using namespace std; #define MOD 20090717 const int maxn = 110; const int sigma_size = 26; const int N = 26; const int maxm = 1 << 10; //状态压缩表示单词 int num[maxm]; //num[i]表示i的二进制中1的个数 struct AC { int ch[maxn][sigma_size]; int val[maxn]; //val[i]表示状态i包含的单词 int sz; int idx(char c) {return c - 'a';} void init() {memset(ch[0] , 0 , sizeof(ch[0])); val[0] = 0; sz = 1;} void insert(string const &s , int v) { int len = s.length() , cur = 0; for(int i = 0; i < len; i++){ int u = idx(s[i]); if(!ch[cur][u]) { memset(ch[sz] , 0 , sizeof(ch[sz])); val[sz] = 0; ch[cur][u] = sz++; } cur = ch[cur][u]; } val[cur] = 1 << v; } int f[maxn]; void getfail() { queue<int> Q; for(int i = 0; i < sigma_size; i++){ int u = ch[0][i]; if(u) {Q.push(u); f[u] = 0;} } while(!Q.empty()) { int cur = Q.front(); Q.pop(); for(int i = 0; i < sigma_size; i++) { int v = ch[cur][i]; if(!v) {ch[cur][i] = ch[f[cur]][i]; continue;} Q.push(v); int j = f[cur]; while(j && !ch[j][i]) j = f[j]; f[v] = ch[j][i]; val[v] |= val[f[v]]; } } } //dp[i][j][k]表示长度为i的串,到达状态j,包含k的word时的数量 int dp [maxn][maxm]; int DP(int n , int m , int p) { memset(dp , 0 , sizeof(dp)); dp[0][0][0] = 1; //dp[i+1][v][k|val[v]] += dp[i][j][k],v是j的后继状态 for(int i = 0; i < n; i++) for(int j = 0; j < sz; j++) { for(int k = 0; k < (1<<m); k++) if(dp[i][j][k]) { for(int x = 0; x < sigma_size; x++) { int v = ch[j][x]; dp[i+1][v][k|val[v]] += dp[i][j][k]; dp[i+1][v][k|val[v]] %= MOD; } } } int ans = 0; for(int i = 0; i < (1<<m); i++) if(num[i] >= p) { for(int j = 0; j < sz; j++) ans = (ans + dp [j][i]) % MOD; } return ans; } }; AC ac; int main() { for(int i = 0; i < maxm; i++) { int tmp = i , cnt = 0; while(tmp) {cnt += tmp & 1; tmp >>= 1;} num[i] = cnt; } int n , m , p; while(cin >> n >> m >> p && n) { ac.init(); for(int i = 0; i < m; i++) { string s; cin >> s; ac.insert(s , i); } ac.getfail(); cout << ac.DP(n , m , p) << endl; } return 0; }
相关文章推荐
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- LFC1.0.0 版本发布
- Android px、dp、sp之间相互转换
- android中像素单位dp、px、pt、sp的比较
- Android对px和dip进行尺寸转换的方法
- Android根据分辨率进行单位转换-(dp,sp转像素px)
- android 尺寸 dp,sp,px,dip,pt详解
- DP问题各种模型的状态转移方程
- POJ-1695-Magazine Delivery-dp
- nyoj-1216-整理图书-dp
- TYVJ1193 括号序列解题报告
- 对DP的一点感想
- TYVJ上一些DP的解题报告
- soj1005. Roll Playing Games
- 01背包问题
- LeetCode之Maximum Product Subarray
- DP Flow
- zoj3605 Find the Marble(三维dp)
- Word Break I,II, Triangle,Palindrome Partitioning 动态规划 DP