大概知道两种 DP 方法。(\(n\log n\) 做法太神了)
方法一:
设 \(f_i\) 表示 \(t\) 在 \(s[1\cdots i]\) 最多出现次数。当最后一位不会被匹配时,答案为 \(f_{i-1}\),否则设答案为 \(g_i\)。考虑下一次出现的位置,要么在 \(i-m+1\) 之前,要么是满足 \([i-m+1\cdots j]=[2i-m-j+1,i]\) 的位置,直接跳 next 即可。
\(f_i=\max\{f_{i-1},g_i\}\)
\(g_i=\max\{f_{i-m},g_{j}+1\}\)
复杂度 \(\mathcal O(|s|\cdot|t|)\)
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
char buf[1<<14],*p1=buf,*p2=buf;
#define GetC() ((p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<14,stdin),p1==p2)?EOF:*p1++)
struct Ios{}io;
template <typename _tp>
Ios &operator >>(Ios &in,_tp &x){
x=0;int w=0;char c=GetC();
for(;!isdigit(c);w|=c=='-',c=GetC());
for(;isdigit(c);x=x*10+(c^'0'),c=GetC());
if(w) x=-x;
return in;
}
const int N=1e5+5,inf=1e9;
char s[N],t[N];
int nxt[N];
int f[N],g[N];
int n,m;
bool check(int i,int j=1){
for(int x=0;x<m;++x){
if(s[i+x]!=t[j+x]&&s[i+x]!='?') return false;
}
return true;
}
int main(){
scanf("%s%s",s+1,t+1);
n=strlen(s+1),m=strlen(t+1);
int j=0;
for(int i=2;i<=m;++i){
while(j&&t[j+1]!=t[i]) j=nxt[j];
if(t[j+1]==t[i]) ++j;
nxt[i]=j;
}
for(int i=m;i<=n;++i){
g[i]=-inf;
if(check(i-m+1)){
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-1],g[i]);
}
printf("%d\n",f[n]);
return 0;
}
方法二:
设 \(dp_{i,j}\) 表示当主串指针为 \(i\),模式串指针为 \(j\) 时的最大出现次数。
对于 ?
枚举填什么。
转移时用 KMP 自动机 实现即可。复杂度 \(\mathcal O(|s|\cdot |t| \cdot |\Sigma|)\)
标签:Berland,int,Anthem,GetC,char,cdot,mathcal,include,CF808G From: https://www.cnblogs.com/pref-ctrl27/p/16993375.html