您的位置:首页 > 其它

UVALive 3490 (LA 3940) || ZOJ 2619 Generator AC自动机(或KMP) + 整数高斯消元 + 数学期望

2015-02-12 20:55 537 查看
题目大意:

就是现在一个字符串生成器每次随机扔出前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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息