G. Anthem of Berland - kmp+DP
G. Anthem of Berland
https://codeforces.com/contest/808/submission/156274622
题意
给定两个字符串 s,t, s中可含有'?'问号可以由任何字母代替
将s中的所有'?'用某个字母代替后 s中最多可以匹配多少个t (可以重叠部分)
思路
用dp[i]表示s的前i个字符最多可匹配的t串的数量
用b[i]表示s的前i个位置 第i个位置与t匹配情况下的t出现的最大次数
用nx[]数组预处理t的最大前后缀匹配长度 因为当有相同前后缀的时候可能会有重叠的t在重叠的情况下可能匹配的更多 转移的情况就是b[i] = max(b[i], b[i - t.size() + k])了
用动态规划 实现b数组和dp数组填充
#include<bits/stdc++.h>
#include<unordered_map>
#define ll long long
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-4;
const ll N = 1e6 + 5;
const int M = 1e5 + 5;
const int mod = 1e9 + 7;
ll n, m, q;
ll a[N], nx[N], dp[N], b[N];
string s, t;
//用kmp预处理nx数组
void getnx(string s) {
ll k = -1, j = 0;
nx[0] = -1;
while (j < s.size()) {
if (k == -1 || s[k] == s[j]) {
k++;
j++;
nx[j] = k;
}
else k = nx[k];
}
}
//判断当前p位置开始能否匹配一个t
bool check(ll p) {
for (int i = p, j = 0; i < s.size() && j < t.size(); i++, j++) {
if (s[i] != t[j] && s[i] != '?') return false;
}
return true;
}
void solve() {
cin >> s >> t;
getnx(t);
int flag = 1, cnt = 0, cnt2 = 0, f, fg = 0;
if (s.size() < t.size()) {
cout << 0 << "\n";
return;
}
for (int i = 0; i <= s.size() - t.size(); i++) {
//先将前一个dp值转移过来
dp[i + t.size()] = dp[i + t.size() - 1];
//判断以p为起点能否与t匹配
if (check(i)) {
//先将b[i + t.size()]赋值为dp[i]因为一定包含dp[i]的值
b[i + t.size()] = dp[i];
//用nx数组取出可转移成b[i + t.size()]的最优情况
for (int k = nx[t.size()]; k >= 0; k = nx[k]) {
b[i + t.size()] = max(b[i + t.size()], b[i + k]);
}
//当前位置可匹配的再加1
b[i + t.size()]++;
//取与不取择优(即是否将i开始的一串匹配成t)
dp[i + t.size()] = max(dp[i + t.size()], b[i + t.size()]);
}
//cout << dp[i + 1] << " ";
}
//输出
cout << dp[s.size()] << "\n";
}
signed main()
{
IOS;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
}