CF808G Anthem of Berland 解题报告

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

 

posted @ 2023-01-09 19:17  LikC1606  阅读(33)  评论(0)    收藏  举报