Description
给定 $s$ 串和 $t$ 串,其中 $s$ 串包含小写字母和问号,$t$ 串只包含小写字母。
假设共有 $k$ 个问号。
你需要给把每个问号变成一个小写字母,共有 $26^k$ 种可能。
对于每种可能,设 $t$ 匹配 $s$ 的次数为 $f_i$,请输出 $\max(f_i)$。
Solution
发现 $|s|* |t| \leq 10^7$, 显然可以对于 $s$ 的每个位置对 $t$ 进行暴力匹配。
于是这是个线性问题,考虑 dp。用 $f_i$ 表示 $t$ 在 $s$ 的前 $i$ 个位置出现的最大次数。
如果当前位置能够匹配,显然 $f_i$ 可以从 $f_{i-m}$ 转移。但这是不够的,因为 $t$ 是可以重叠放的,而且仅仅有一个 $f$ 数组是不够的,于是我们可以用 $g_i$ 表示第 $i$ 个位置一定选的最大匹配数。枚举 $t$ 的 border,每次更新 $g_i$ 即可。
CODE
#include<bits/stdc++.h> using namespace std; inline int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') f = -f; c = getchar();} while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();} return x * f; } const int N = 1e5 + 10; int n, m, f[N], g[N], nxt[N]; char s[N], t[N]; inline bool chk (int p) { if (p < m) return false; for (int i = 1; i <= m; ++ i) if (s[p-i+1] != t[m-i+1] && s[p-i+1] != '?') return false; return true; } int main () { scanf("%s", s + 1); scanf("%s", t + 1); n = strlen(s + 1); m = strlen(t + 1); nxt[1] = 0; for (int i = 2, j = 0; i <= m; ++ i) { while (j && t[i] != t[j+1]) j = nxt[j]; if (t[i] == t[j+1]) ++ j; nxt[i] = j; } for (int i = 1; i <= n; ++ i) { f[i] = f[i-1]; if (chk(i)) { g[i] = f[i-m] + 1; for (int j = nxt[m]; j; j = nxt[j]) g[i] = max(g[i], g[i-(m-j)] + 1); f[i] = max(f[i], g[i]); } } printf("%d\n", f[n]); return 0; }View Code
标签:匹配,Berland,小写字母,Anthem,while,getchar,CF808G,问号 From: https://www.cnblogs.com/CikL1099/p/17038324.html