UVa 11468 Substring

        题意:给出一些字符和对应的选择概率,随机选择L次后得到一个长度为L的随机字符串S。给k个模板串,计算S不包含任何一个串的概率。

        思路:AC自动机+概率dp。之前我做这题一直WA,然后稍微改改代码交了下杭电上类似的题目居然1A了。。后来我才发现原因,是搜到字典树中间非叶子节点了以后,也应该试着往回跳,看看能不能跳到某个模板的末尾。但是杭电那题只有一个模板,所以才A掉的。不过杭电的题目背景比较有意思,好像是一个我小时候听过的故事,如果一个猴子随机敲键盘,可以敲出世界上任何一篇文章。。。

        回到正题。这题先建立一个AC自动机,然后从字典树树根开始,走L步,如果走到模板串的末尾,就不能再走下去了,边走边算概率。我是用递推dp来做的,dp(i,j)表示走到字典树的第i个节点,已经走了j步的概率。毕竟dp(i,j)有多条路线可以到达,全部加在一起算比较省时间,如果一条路走L步走到底会超时的。要注意的是,走到某个节点时,它本身不是模板串末尾并不代表可以继续走下去,应该往回跳看看是否会遇到模板串的末尾。最后结果是sum(dp(i,L))(i取所有的树节点)。


#include <iostream>             
#include <stdio.h>             
#include <cmath>             
#include <algorithm>             
#include <iomanip>             
#include <cstdlib>             
#include <string>             
#include <memory.h>             
#include <vector>             
#include <queue>             
#include <stack>             
#include <map>           
#include <set>           
#include <ctype.h>           
#define INF 1<<30         
#define ll long long         
#define max3(a,b,c) max(a,max(b,c))         

using namespace std;

#define maxnode 1010

int T; //50
int k; //20;
int n; //
int L;
bool valid[65];
double p[65];
double dp[maxnode][110];

int char2int(char c){
	if(c>='a'&&c<='z')return c-'a';
	if(c>='A'&&c<='Z')return c-'A'+26;
	if(c>='0'&&c<='9')return c-'0'+52;
	return -1; 
}

// ac
int val[maxnode];
int vis[maxnode][110];
int ch[maxnode][65];
int next[maxnode];
int sz;
double ans;

void init(){
	sz=1;
	memset(ch[0],0,sizeof(ch[0]));
	memset(next,0,sizeof(next));
	memset(val,0,sizeof(val));
}

void insert(char* str){
	int u=0;
	int len=strlen(str);
	for(int i=0;i<len;i++){
		int v=char2int(str[i]);
		if( !ch[u][v] ){
			ch[u][v]=sz;
			memset(ch[sz],0,sizeof(ch[sz]));
			val[sz]=0;
			sz++;
		}
		u=ch[u][v];
	}
	val[u]++;
}

void build_ac(){
	memset(next,0,sizeof(next));
	queue<int> que; que.push(0);
	while(!que.empty()){
		int u=que.front(); que.pop();
		for(int i=0;i<62;i++){
			int v=ch[u][i];
			if(!v)continue;
			if(!u){
				next[v]=0;
				que.push(v);
			}else{
				int k=next[u];
				while(k&&!ch[k][i]){
					k=next[k];
				}
				if(ch[k][i])k=ch[k][i];
				next[v]=k;
				//cout<<"next "<<v<<" = "<<k<<endl;
				que.push(v);
			}
		}
	}
}
//ac end

int main(){
	scanf("%d",&T);
	int cas=0;
	while(T--){
		cas++;
		memset(valid,0,sizeof(valid));
		memset(vis,0,sizeof(vis));
		ans=0;
		init();
		//
		scanf("%d",&k);
		for(int i=1;i<=k;i++){
			char pattern[30];
			scanf("%s",pattern);
			insert(pattern);
		}
		build_ac();
		//
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			char a;
			double b;
			cin>>a>>b;
			valid[char2int(a)]=1;
			p[char2int(a)]=b;
		}
		//
		scanf("%d",&L);
		
		for(int i=0;i<maxnode;i++){
			for(int j=0;j<=L;j++)dp[i][j]=0.0;
		}
		dp[0][0]=1.0; vis[0][0]=1;
		for(int i=1;i<=L;i++){
			for(int j=0;j<maxnode;j++){
				if(!vis[j][i-1])continue;
				for(int k=0;k<62;k++){
					if(!valid[k])continue;
					int v=j;
					while(v&&!ch[v][k]){
						v=next[v];
					}
					if(ch[v][k])v=ch[v][k];
					
					int tmp=v;
					bool skip=0;
					while(tmp){
						if(val[tmp]){
							skip=1;
							break;
						}
						tmp=next[tmp];
					}
					if(skip)continue;
					
					dp[v][i]+=dp[j][i-1]*p[k];
					vis[v][i]=1;
				}
			}
		}
		
		for(int i=0;i<maxnode;i++){
			ans+=dp[i][L];
		}
		
		printf("Case #%d: %.6lf\n",cas,ans);
	}
	return 0;
}



你可能感兴趣的:(AC自动机)