CF808G Anthem of Berland

\(\mathcal Link\)

大概知道两种 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|)\)

posted @ 2022-12-19 23:28  pref_ctrl27  阅读(42)  评论(0编辑  收藏  举报