UVA11468子串 AC自动机+概率DP

最近在学习字符串算法,学到KMP和AC自动机总有种似懂非懂的感觉,这个题的思路也是看的STD

KMP

个人感觉KMP的精髓就是失配函数,在当前匹配失败的情况下不必重头推倒再来,而是从特定的位置。i位置的失配函数为f[i],可表示i位置失配后去f[i]位置找,是因为f[i]位置之前的模板串与i位置前的模板串的等长后缀是相等的,这样做下去当发现整个模板串都遍历完成,就是找到了模板串作为目标串子串的结束位置

AC自动机

AC自动机是用来处理多个模板串的,所以就用到了Trie树。个人认为AC自动机是Trie树和KMP结合,或者说是特殊版的Trie树。当用目标串在Trie树中查找时,如果走到当前节点A后,发现没有目标串下一个字符a的节点,就沿着失配边一直跳,直到到根或者到有子节点a的节点B,B就是需要重新开始的起点

不必向上找失配边的操作

算法竞赛入门经典这本书上写了两种做法,这个题需要用第二种,就是getfail时,枚举节点A所有可能的下一个节点B,如果A走不到B,那么不是将B跳过,因为这样的话查找的时候从A找不到节点B还要跳失配边直到根或者到有子节点B的节点。

所以搞一个类似于并茶几路径压缩的操作,即A走不到B,就把SON【A】【B】设为A沿着失配边向上的点的对应B节点的位置,就相当于自动跳了一次失配边

这题做法不写了,算法竞赛入门经典(蓝书)P218


#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define fr(i,s,t) for (i=s;i<=t;i++)
#define Clear(a) memset(a,0,sizeof(a))
using namespace std;
int K,N,L,Size,son[20000][70],f[2000];
char S[200];
double pro[200],ans,dp[20000][200];
bool v[2000],vis[20000][200];
int id(char c){
	if (c>='A'&&c<='Z') return c-'A';
	if (c>='a'&&c<='z') return 26+c-'a';
	return 52+c-'0';
}
void Insert(){
	int rt=0,d,i;
	for (i=0;i<strlen(S);i++){
		d=id(S[i]);
		if (!son[rt][d]) son[rt][d]=++Size;
		rt=son[rt][d];
	}
	v[rt]=1;
}
void Getfail(){
	queue <int> Q;
	int i,u,y,x;
	for (i=0;i<63;i++){
		u=son[0][i];
		if (u) Q.push(u);
	}
	while (!Q.empty()){
		x=Q.front(); Q.pop();
		for (i=0;i<63;i++){
			u=son[x][i];
			if (!u){son[x][i]=son[f[x]][i];continue;}
			Q.push(u);
			y=f[x];
			while (y&&!son[y][i]) y=f[y];
			f[u]=son[y][i];
			v[u]|=v[f[u]];
		}
	}
}
double Dfs(int x,int L){
	if (!L) return 1.0;
	if (vis[x][L]) return dp[x][L];
	vis[x][L]=1;
	double &res=dp[x][L];
	res=0;
	for (int i=0;i<63;i++)
		if (!v[son[x][i]]&&pro[i])
			res+=pro[i]*Dfs(son[x][i],L-1);
	return res;
}
void Work(int Num){
	Clear(pro); Clear(dp);  ans=0; 
	Clear(son); Size=0; Clear(vis); Clear(f); Clear(v);
	scanf("%d",&K);
	int i;
	fr(i,1,K)
		cin>>S,Insert();
	scanf("%d",&N);
	fr(i,1,N){
		char ch; cin>>ch;
		scanf("%lf",&pro[id(ch)]);
	}
	Getfail();
	scanf("%d",&L);
	ans=Dfs(0,L);
	
	printf("Case #%d: %0.6lf\n",Num,ans);
}
int main(){
	int T,i;
	scanf("%d",&T);
	fr(i,1,T) Work(i);
	return 0;
}



猜你喜欢

转载自blog.csdn.net/lerbon23james/article/details/79758498