• 题解-UVA1401 Remember the Word


    知识点:dp+trie

    这道题显然是从前往后dp的,题解区里还没有这样的题解,我就来发一波(题解区里也有提到不过并没有做详细的说明也没有代码)。

    思路和从后往前的基本一样。

    (dp_i)代表前缀 (s_{1...i}) 有多少种不同的组成方式。

    很容易想到转移方程:

    如果一个模式串 (t) 是前缀 (s_{1...i}) 的后缀,则 (dp_i += dp_{i-|t|})

    如果暴力去判断的话复杂度是 (O(nm)) 的,这道题过不去,所以我们要考虑优化。

    我们发现每个模式串最长也才 (100),考虑将所有模式串倒序建成一个 (trie)

    对于 (s) 的一个前缀,将其倒叙放到 (trie) 上匹配,如果经过结束节点就说明匹配到了一个模式串,(dp) 值加上其即可。

    因为模式串最长 (100),即 (trie) 树的深度最大才 (100),所以复杂度是(O(100n))的。

    多组数据注意初始化。

    #include <bits/stdc++.h>
    using namespace std;
    int trie[400010][26], tot;
    char s[300010], t[110];
    int n, m;
    int End[400010], dp[300010];
    int cnt;
    int main() {
    	while (scanf("%s", s + 1) != EOF) {
    		tot = 1;
    		memset(End, 0, sizeof(End));
    		memset(trie, 0, sizeof(trie));
    		cnt++;
    		n = strlen(s + 1);
    		scanf("%d", &m);
    		for (int i = 1, len, p; i <= m; i++) {
    			scanf("%s", t + 1);
    			len = strlen(t + 1);
    			p = 1;
    			for (int j = len; j; j--) {
    				if (!trie[p][t[j] - 'a']) trie[p][t[j] - 'a'] = ++tot;
    				p = trie[p][t[j] - 'a'];
    			}
    			End[p]++;
    		}
    		memset(dp, 0, sizeof(dp));
    		dp[0] = 1;
    		for (int i = 1, p; i <= n; i++) {
    			p = 1;
    			for (int j = i; j; j--) {
    				if (trie[p][s[j] - 'a']) p = trie[p][s[j] - 'a'];
    				else break;
    				if (End[p]) {
    					dp[i] += (dp[j - 1] * End[p]) % 20071027;
    					dp[i] %= 20071027;
    				}
    			}
    		}
    		printf("Case %d: %d
    ", cnt, dp[n]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Codeforces Round #260 (Div. 2)
    面试题:给定数组a,找到最大的j-i, 使a[j]>a[i]
    ssh自动输入密码脚本 切换目录脚本
    make工作时的执行步骤
    Codeforces Round #259 (Div. 2)
    Codeforces Round #258 (Div. 2)
    如何在半径为1的圆中随机选取一个点
    面试中常问的有关随机选取k个数的总结
    topcoder SRM 628 DIV2 BracketExpressions
    topcoder SRM 628 DIV2 BishopMove
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/14901189.html
Copyright © 2020-2023  润新知