Codeforces 808G Anthem of Berland - KMP - 动态规划

题目传送门

  传送点I

  传送点II

  传送点III

题目大意

  给定一个字符串$s$,和一个字符串$t$,$t$只包含小写字母,$s$包含小写字母和通配符'?'。询问$t$可能在$s$中出现最多多少次。

  原来觉得挺神仙,现在觉得还好。

  显然用$g_{i}$表示在匹配到第$i$个字符,最多能匹配$t$的次数。

  现在讨论它的转移,需要考虑它和前一个匹配有没有重叠。

  1. 如果没有重叠,直接从$g_{i - |t|}$转移。
  2. 如果有重叠,上一个可能的匹配的结束位置显然是可以枚举的,通过跳$fail$就能找到。我们记一个$f_{i}$表示恰好最后一个匹配在$i$处结束时的最多匹配次数。然后就可以转移了。

Code

 1 /**
 2  * Codeforces
 3  * Problem#808G
 4  * Accepted
 5  * Time: 46ms
 6  * Memory: 1400k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 typedef bool boolean;
11 
12 const int N = 1e5 + 5;
13 
14 int n, m;
15 char S[N], T[N];
16 
17 inline void init() {
18     scanf("%s%s", S + 1, T + 1);
19     n = strlen(S + 1);
20     m = strlen(T + 1);
21 }
22 
23 int fail[N];
24 void kmp() {
25     fail[0] = fail[1] = 0;
26     for (int i = 1, j; i <= m; i++) {
27         j = fail[i];
28         while (j && T[i + 1] != T[j + 1])
29             j = fail[j];
30         fail[i + 1] = ((T[i + 1] == T[j + 1]) ? (j + 1) : (0));
31     }
32 }
33 
34 boolean match(char *str) {
35     for (int i = 1; i <= m; i++)
36         if (str[i] != T[i] && str[i] != '?')
37             return false;
38     return true;
39 }
40 
41 int f[N], g[N];    // f: maximum match times when ends at i, g: maximum match times when ends before i ant at i.
42 inline void solve() {
43     kmp();
44     for (int i = m; i <= n; i++) {
45         if (match(S + (i - m))) {
46             f[i] = max(f[i], g[i - m] + 1);
47             for (int j = fail[m]; j; j = fail[j])
48                 f[i] = max(f[i], f[i - m + j] + 1);
49         }
50         g[i] = max(f[i], g[i - 1]);    
51     }
52     printf("%d\n", g[n]);
53 }
54 
55 int main() {
56     init();
57     solve();
58     return 0;
59 }
posted @ 2017-07-26 17:35  阿波罗2003  阅读(...)  评论(... 编辑 收藏