题意:给你 k 个模板串,然后给你一些字符的出现概率,然后给你一个长度 l ,问你这些字符组成的长度为 l 的字符串不包含任何一个模板串的概率。
思路:AC自动机+概论DP
首先用K个模板构造好AC自动机。题目上说长L的新串的子串不包含任何一个K串,其实就是说在构造好的树中,从根往下走L步都不包含K个模板。此题用match标记是否为K模板串。
状态转移方程代码中注释了。
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<cstdio> 5 #include<map> 6 #include<string> 7 using namespace std; 8 9 const int SIGMA_SIZE = 64; 10 const int MAXNODE = 500; // 结点总数 11 const int MAXS = 20 + 10; // 模板个数 12 13 int idx[256], n; 14 double prob[SIGMA_SIZE]; 15 16 struct AhoCorasickAutomata 17 { 18 int ch[MAXNODE][SIGMA_SIZE]; 19 int f[MAXNODE]; // fail函数 20 int match[MAXNODE]; // 是否包含某一个字符串 21 int sz; // 结点总数 22 23 void init() 24 { 25 sz = 1; 26 memset(ch[0], 0, sizeof(ch[0])); 27 } 28 29 // 插入字符串 30 void insert(char *s) 31 { 32 int u = 0, n = strlen(s); 33 for(int i = 0; i < n; i++) 34 { 35 int c = idx[s[i]]; 36 if(!ch[u][c]) 37 { 38 memset(ch[sz], 0, sizeof(ch[sz])); 39 match[sz] = 0; 40 ch[u][c] = sz++; 41 } 42 u = ch[u][c]; 43 } 44 match[u] = 1; 45 } 46 47 // 计算fail函数 48 void getFail() 49 { 50 queue<int> q; 51 f[0] = 0; 52 // 初始化队列 53 for(int c = 0; c < SIGMA_SIZE; c++) 54 { 55 int u = ch[0][c]; 56 if(u) 57 { 58 f[u] = 0; 59 q.push(u); 60 } 61 } 62 // 按BFS顺序计算fail 63 while(!q.empty()) 64 { 65 int r = q.front(); 66 q.pop(); 67 for(int c = 0; c < SIGMA_SIZE; c++) 68 { 69 int u = ch[r][c]; 70 if(!u) 71 { 72 ch[r][c] = ch[f[r]][c]; 73 continue; 74 } 75 q.push(u); 76 int v = f[r]; 77 while(v && !ch[v][c]) v = f[v]; 78 f[u] = ch[v][c]; 79 match[u] |= match[f[u]]; 80 } 81 } 82 } 83 }; 84 85 AhoCorasickAutomata ac; 86 87 double d[MAXNODE][105]; 88 int vis[MAXNODE][105]; 89 90 double getProb(int u, int L)//d[u][L]=prob[u]*d[v][L-1]状态转方程 v为u的儿子可以走节点 91 { 92 if(!L) return 1.0; 93 if(vis[u][L]) 94 return d[u][L]; 95 vis[u][L] = 1; 96 d[u][L]=0.0; 97 for(int i = 0; i < n; i++) 98 if(!ac.match[ac.ch[u][i]]) 99 d[u][L] += prob[i] * getProb(ac.ch[u][i], L-1); 100 return d[u][L]; 101 } 102 103 char s[30][30]; 104 105 int main() 106 { 107 int T; 108 scanf("%d", &T); 109 for(int kase = 1; kase <= T; kase++) 110 { 111 int k, L; 112 scanf("%d", &k); 113 for(int i = 0; i < k; i++) scanf("%s", s[i]); 114 scanf("%d", &n); 115 for(int i = 0; i < n; i++) 116 { 117 char ch[9]; 118 scanf("%s%lf", ch, &prob[i]); 119 idx[ch[0]] = i; 120 } 121 ac.init(); 122 for(int i = 0; i < k; i++) ac.insert(s[i]); 123 ac.getFail(); 124 scanf("%d", &L); 125 memset(vis, 0, sizeof(vis)); 126 memset(d,0,sizeof(d)); 127 printf("Case #%d: %.6lf\n", kase, getProb(0, L)); 128 } 129 return 0; 130 }
时间: 2024-10-05 08:27:15