题目大意:
就是现在一个字符串生成器每次随机扔出前n(n <= 26)个大写英语字母的一个
将产生的字符连接起来成为其生成的字符串,如果它产生的字符串中有连续的一段出现了给定的禁止串,则生成停止
求停止时已经生成的字符串长度的期望
大致思路:
一开始果断用了AC自动机,后来发现KMP也就足够了
这个题建立方程组之后用Gauss消元不能用double的,容易产生误差...(因为误差跪了好多发之后改成整数版)
状态转移方程和细节见代码注释..
代码如下:
Result : Accepted Memory : 276 KB Time : 0 ms
/* * Author: Gatevin * Created Time: 2015/2/12 18:23:00 * File Name: Mononobe_Mitsuki.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; int n; char in[15]; /* * 首先建立AC自动机得到状态转移图 * 用E[i]表示在转移图中的点i处到达目标状态需要的期望步数, 则E[0]即为解(root == 0) * 由于只有一个串记其结尾位置为AC自动机中的点L - 1长度为L的话 * 显然E[L - 1] = 0 (L为AC自动机上的总点数) * 对于0 <= i < L - 1有转移方程E[i] = ∑(E[next[i][j]] + 1) / n , (0 <= j < n) * L - 1 <= 12可以使用高斯消元求解复杂度O(L^3) AC自动机复杂度O(L) * 由于此题有误差,使用double的高斯消元求得E[0]后转long long误差太大不可行 * 需要用整数型的高斯消元 */ struct Trie { int next[15][26], fail[15]; bool end[15]; int L, root; int var, equ; lint a[15][15], x[15]; int newnode() { for(int i = 0; i < n; i++) next[L][i] = -1; end[L++] = 0; return L - 1; } void init() { L = 0, equ = 0, var = 0; root = newnode(); return; } void insert(char *s) { int now = root; for(; *s; s++) { if(next[now][*s - 'A'] == -1) next[now][*s - 'A'] = newnode(); now = next[now][*s - 'A']; } end[now] = 1; return; } void build() { fail[root] = root; queue <int> Q; Q.push(root); while(!Q.empty()) { int now = Q.front(); Q.pop(); for(int i = 0; i < n; i++) if(next[now][i] == -1) next[now][i] = now == root ? root : next[fail[now]][i]; else { fail[next[now][i]] = now == root ? root : next[fail[now]][i]; Q.push(next[now][i]); } } return; } void getEquationSystem() { memset(a, 0, sizeof(a)); var = L; for(int i = 0; i < L - 1; i++) { a[equ][i] += n; for(int j = 0; j < n; j++) a[equ][next[i][j]] -= 1; x[equ++] = n; } a[equ][L - 1] = 1; x[equ++] = 0; return; } lint Gauss()//表示用double版本的高斯消元求解,最后还原成整数的方法因为误差跪掉了.... { for(int i = 0; i < equ; i++) { int r = i; while(r < equ && !a[r][i]) r++; if(r != i)//找到第r列不是0的之后交换至r行 { for(int j = 0; j < var; j++) swap(a[r][j], a[i][j]); swap(x[r], x[i]); } for(int k = i + 1; k < equ; k++) if(a[k][i])//将下面所有行的第i列变成0 { lint tmp = a[k][i];//两组互相乘上对面的第i列的数作差即可 for(int j = i; j < var; j++) a[k][j] = a[k][j]*a[i][i] - tmp*a[i][j]; x[k] = x[k]*a[i][i] - tmp*x[i]; } } //对剩下的三角矩阵递推求解 for(int i = equ - 1; i >= 0; i--) { for(int j = i + 1; j < var; j++) x[i] -= x[j]*a[i][j]; x[i] /= a[i][i]; } return x[0]; } }; Trie AC; int main() { int T; scanf("%d", &T); for(int cas = 1; cas <= T; cas++) { scanf("%d", &n); AC.init(); scanf("%s", in); AC.insert(in); AC.build(); AC.getEquationSystem(); printf("Case %d:\n%lld\n", cas, AC.Gauss()); if(cas != T) printf("\n"); } return 0; }