原来觉得挺神仙,现在觉得还好。
显然用$g_{i}$表示在匹配到第$i$个字符,最多能匹配$t$的次数。
现在讨论它的转移,需要考虑它和前一个匹配有没有重叠。
- 如果没有重叠,直接从$g_{i - |t|}$转移。
- 如果有重叠,上一个可能的匹配的结束位置显然是可以枚举的,通过跳$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 ", g[n]); 53 } 54 55 int main() { 56 init(); 57 solve(); 58 return 0; 59 }