hdoj 3058 ac自动机+高斯消元

这道题和zoj2619的实质性是一样的,只不过这个中字符串的个数比较多,所以就需要建立自动机了,我的zoj上的题是直接用kmp的,只不过这道题麻烦却让我纠结了几天啊,感觉有的时候脑子就是不太会拐弯,就是不会转化的。

不知道怎么用ac自动机去转化然后去建立数组,然后就今天下午在这里改了几乎一下午,挺搞笑的,一有思路就在那里改,然后改的多了就又回去找最原始的代码继续弄,好歹还是改出来了,现在自己想想思路都是迷糊的啊,其实是看了别人的得出的方程然后启发下改出来的。

思路:关于ac自动机的建立我就不罗嗦了,就是关于方程的转换问题,一开始我想的是在所有的字符串中相同的长度的字符进行累加放在一行,但是一直wa,然后就没有什么想法,到后来改为对没有个节点进行标记,一个节点记录一行,然后就AC了,不过当时只顾着似乎对的兴奋没有怎么改数组的大小然后RE了一次,囧啊!下面是代码,可能改的有点乱啊!

/
// File Name: 21631.cpp
// Author: wang
// mail: 
// Created Time: 2013-8-8 8:29:19
/
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <cstring>
#include <cmath>

#include <algorithm>
#include<iostream>
#include<queue>
#include <map>
using namespace std;
typedef long long ll;
#define INF (INT_MAX/10)
#define SQR(x) ((x)*(x))
#define rep(i, n) for (int i=0; i<(n); ++i)
#define repf(i, a, b) for (int i=(a); i<=(b); ++i)
#define repd(i, a, b) for (int i=(a); i>=(b); --i)
#define clr(ar,val) memset(ar, val, sizeof(ar))
#define pb(i) push_back(i)
#define exp 0.000000001
#define N 101
double a[N][N];
int n,m,tot;
char s[20];
bool vis[50];
vector<int>vec;//存储出现的字符
struct node
{
	bool sign;
	int len,fal;
	int next[26];
};
node ac[10000];

void init()
{
	ac[0].sign=false;tot=0;
	ac[0].len=0; ac[0].fal=0;
	memset(ac[0].next,-1,sizeof(ac[0].next));
	memset(a,0,sizeof(a));
	memset(vis,false,sizeof(vis));
	vec.clear();
}

void tree()
{
	int len=strlen(s);
	int u=0;
	rep(i,len)
	{
		int x=s[i]-'A';
		if(ac[u].next[x]==-1)
		{
            ++tot; ac[tot].sign=false; 
			memset(ac[tot].next,-1,sizeof(ac[tot].next));
			ac[u].next[x]=tot;
		}
		u=ac[u].next[x];
		if(i==len-1) ac[u].sign=true;//代表有以这个结尾的
	}
}
int fail(int u,int k)
{
	if(ac[u].next[k]!=-1) return ac[u].next[k];
	if(u==0) return 0;
	return fail(ac[u].fal,k);
}
void bulid()
{
	queue<int>q;
	q.push(0);
	while(!q.empty())
	{
		int x=q.front(); q.pop();
		rep(i,26)
			if(ac[x].next[i]!=-1)
			{
				q.push(ac[x].next[i]);
				if(x==0) ac[ac[x].next[i]].fal=0;
				else ac[ac[x].next[i]].fal=fail(ac[x].fal,i);
			}
	}
	int len=0;
	q.push(0);
	while(!q.empty())
	{
		int x=q.front(); q.pop();
		if(ac[ac[x].fal].sign==true)
			ac[x].sign=true;
		if(ac[x].sign==false)
			ac[x].len=len++;
		rep(i,26)
			if(ac[x].next[i]!=-1)
				q.push(ac[x].next[i]);
	}
}
void work()//如果为true就代表是结尾就不要加了
{ 
     queue<int>q; q.push(0);
	 int len=vec.size();
	 int Max=0;
	 while(!q.empty())
	 {
		 int x=q.front(); q.pop();
		 int l=ac[x].len;
		 Max=max(Max,l);
		 a[l][l]-=n;//等号左边的,为负号
		 a[l][0]+=(n-len);//会变为0的
		 rep(j,len)
		 {
			 int u=x; int y=vec[j]; 
			 int sign=0;
			 while(true)
			 {
				 if(ac[u].next[y]!=-1)
				 {
					 if(ac[ac[u].next[y]].sign==false)
                     a[l][ac[ac[u].next[y]].len]+=1;
						 sign=1;
					 break;
				 }
				 if(ac[u].next[y]!=-1) sign=1;
				 if(u==0) break;
				 u=ac[u].fal;
			 }
			 if(sign==0)
				 a[l][0]++;
		 }
		 rep(i,26) 
			 if(ac[x].next[i]!=-1 && ac[ac[x].next[i]].sign==false)
				 q.push(ac[x].next[i]);
	 }
	 repf(i,0,Max) a[i][Max+1]=n;
	 n=Max+1,m=Max+2;

}
void guess()
{
	rep(i,n)
	{
		int k=-1; 
		repf(j,i,n-1)
			if(a[j][i]!=0){
				k=j; break;
			}
		if(k==-1) continue;
		if(k!=i)
			rep(j,m) swap(a[i][j],a[k][j]);
		rep(j,n)
		{
			if(j!=i)
			{
				double x=a[i][i],y=a[j][i];
				rep(k,m)
					a[j][k]=a[j][k]-a[i][k]*y/x;
			}
		}
	}
	if(fabs(0-a[0][0])<exp) printf("%0.00\n");
	else printf("%0.2lf\n",-a[0][m-1]*1.0/a[0][0]);
}
void solve()
{
	init();
	rep(i,m)
	{
		scanf("%s",s); tree();
		int len=strlen(s);
		rep(j,len) 
			if(vis[s[j]-'A']==false)
			{
				vec.pb(s[j]-'A'); vis[s[j]-'A']=true;
			}
	}
	bulid();
//	rep(i,tot+1) cout<<ac[i].sign<<endl;
	work();
	guess();
}

int main()
{
	int test;
	scanf("%d",&test);
	while(test--)
	{
		scanf("%d%d",&n,&m);
        solve();
	}
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

淡定的小Y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值