zoukankan      html  css  js  c++  java
  • 「UVA 11468」Substring

    Description

    给定 (n) 个字符 (c_1, c_2, cdots c_n) 和各自的选择概率 (p_1, p_2, cdots, p_n),随机选择 (L) 次得到的随机字符串 (S) (每次独立随机)。给出 (K) 个模板串 (t_1, t_2, cdots, t_K),计算 (S) 不包含任何一个模板串的概率。

    多组数据,组数为 (T)

    Hint

    • (1le T le 50)
    • (1le Kle 20)
    • (1le |t_i|le 20)
    • (c_i in [ exttt{a}, exttt{z}] cup [ exttt{A}, exttt{Z}] cup [ exttt{0}, exttt{9}])
    • (1le L le 100)
    • (sum_{i = 1}^n p_i = 1)
    • (forall i in [1, K], t_i)(n) 个给定字符组成。

    Solution

    AC 自动机 + 动态规划

    首先肯定是对所有 模板串 建 AC自动机,在模式串结尾标记一下表示不能走。然后按之类问题的套路来:

    (f(i, x)) 为字符串长度为 (i),走到结点 (x) 且不走到标记结点的概率。

    那么自然是 (f(i - 1, x)) 转移到 (f(i, delta(x, c))) 的方向。状态转移方程:

    [f(i, y) = sumlimits_{delta(x, c) = y} f(i - 1, x) imes ext{prob}(c) ]

    其中 ( ext{prob}(c)) 表示选择字符 (c) 的概率。显然如果 (y) 点北标记为结尾就要跳过。


    像这种 dp 会有一个坑:当一个结点的 fail 对应的结点是被标记了的,那么这个结点也应该被标记。为什么呢?因为 fail 指向的是当前的最长后缀,而一个以一个模板串作为后缀的字符串自然也不合法

    Code

    /*
     * Author : _Wallace_
     * Source : https://www.cnblogs.com/-Wallace-/
     * Problem : UVA 11468 Substring
     */
    #include <cctype>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    
    using namespace std;
    const int P_L = 25;
    const int M_L = 105;
    const int K = 25;
    const int S = 62;
    
    int k, n, l;
    double prob[S];
    
    inline int trans(char c) {
    	if (isdigit(c)) return c - '0';
    	else if (islower(c)) return c - 'a' + 10;
    	else return c - 'A' + 36;
    }
    
    namespace ACAM {
    	const int T = K * P_L;
    	struct Node {
    		int ch[S];
    		int fail;
    		bool statu;
    	} t[T];
    	double f[M_L][T];
    	int total;
    	
    	void restart() {
    		using namespace ACAM;
    		memset(f, 0, sizeof f);
    		memset(t, 0, sizeof t);
    		total = 0;
    	}
    	
    	void insert(char* s) {
    		int x = 0;
    		for (register int i = 0; s[i]; i++) {
    			int c = trans(s[i]);
    			if (!t[x].ch[c]) t[x].ch[c] = ++total;
    			x = t[x].ch[c];
    		}
    		t[x].statu = true;
    	}
    	
    	void build_fail() {
    		queue<int> Q;
    		for (register int i = 0; i < S; i++)
    			if (t[0].ch[i]) Q.push(t[0].ch[i]), t[t[0].ch[i]].fail = 0;
    		while (!Q.empty()) {
    			int x = Q.front(); Q.pop();
    			for (register int c = 0; c < S; c++)
    				if (t[x].ch[c]) {
    					Q.push(t[x].ch[c]);
    					t[t[x].ch[c]].fail = t[t[x].fail].ch[c];
    				} else t[x].ch[c] = t[t[x].fail].ch[c];
    			t[x].statu |= t[t[x].fail].statu; // 此坑的解决方案在此体现
    		}
    	}
    	
    	double solve(int l) {
    		f[0][0] = 1.0;
    		for (register int i = 1; i <= l; i++)
    			for (register int x = 0; x <= total; x++)
    				for (register int c = 0; c < S; c++)
    					if (!t[t[x].ch[c]].statu)
    						f[i][t[x].ch[c]] += f[i - 1][x] * prob[c];
    		double ret = 0.0;
    		for (register int i = 0; i <= total; i++)
    			ret += f[l][i];
    		return ret;
    	}
    };
    
    char str[M_L];
    signed main() {	
    	int total_tc;
    	scanf("%d", &total_tc);
    	
    	for (int tc = 1; tc <= total_tc; tc++) {
    		ACAM::restart();
    		memset(prob, 0, sizeof prob);
    		
    		scanf("%d", &k);
    		for (register int i = 1; i <= k; i++) {
    			scanf("%s", str);
    			ACAM::insert(str);
    		}
    		
    		scanf("%d", &n);
    		for (register int i = 1; i <= n; i++) {
    			char lt[5]; double pb;
    			scanf("%s%lf", lt, &pb);
    			prob[trans(lt[0])] += pb;
    		}
    		
    		ACAM::build_fail();
    		scanf("%d", &l);
    		double ans = ACAM::solve(l);
    		
    		printf("Case #%d: %.6f
    ", tc, ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    第二阶段冲刺01
    第十三周进度总结
    单词统计续
    sys模块
    os模块
    random模块
    datetime模块
    time模块
    模块基础
    内置函数
  • 原文地址:https://www.cnblogs.com/-Wallace-/p/12945723.html
Copyright © 2011-2022 走看看