题目意思是给出一个s串,比如??c??????,然后给出一个t串,比如abcab,请你把每个问号都替换成具体的字母,使得t串在s串中有最大匹配数。
题解:
单模式匹配最好用的还是KMP,但是带通配符的怎么办呢?答案是用DP做。
开一个f函数,f[i]记录的是1-i位置上的通配符都被替换成具体的字母后,且t刚好是preffix(s,i)的一个suffix。换句话说,就是以i为尾位置,刚好有一次成功的匹配。那么这个f当且仅当t是preffix(s,i)的一个suffix时才有意义。
开另一个g函数,记录的是考虑到i时候的最大匹配数,不管通配符是否全部替换了,换句话说,某几个特定位置的通配符不管换成什么对答案都没有影响,这是由这几个通配符所在上下文的特殊性决定的。
比如说??c??????的最后一个?换成什么都无所谓。
算法流程:
先KMP处理出t的next数组。
对s的tlen到slen每一个i位置,先看一下t是不是 preffix(s,i)的suffix,如果不是的话,g[i]=g[i-1]结束掉,因为此时的f[i]没有意义。
如果是,我们就要来算一下f[i]了,首先我们知道的信息是,最后一次成功的位置一定是i了,我们现在想用上次匹配成功的那个尾位置来更新一下最大值。我们想,如果说在我这个suffix里边枚举尾位置的话,只可能是next[tlen]或者next[next[tlen]]……那么我们就知道该怎么办了。如果尾位置在suffix之前呢?需要枚举一遍吗?答案是不需要,别忘了我们还有g数组,这个答案已经记录在g[i-tlen]里边了,因为g值preffix(s,i-tlen)的一个最优解,也就是说他考虑了每一个可能的最后的尾位置,这就是我们想要的。那么在这个枚举尾位置更新max结束之后,再把f[i]++,就完成了f[i]的计算。
那么我们还需要计算g[i],显然,g[i]=max(g[i-1],f[i]),意思是最后一个成功的尾位置 在i-1之前 和 在i这里 取一个最大值。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
char s[MAXN],t[MAXN];
int nxt[MAXN],f[MAXN],g[MAXN];
int main(){
scanf("%s",s+1);
scanf("%s",t+1);
int slen=strlen(s+1);
int tlen=strlen(t+1);
for (int i=2,j=0;i<=tlen;i++){
while (j&&t[i]!=t[j+1]) j=nxt[j];
if (t[i]==t[j+1]) ++j;
nxt[i]=j;
}
for(int i=tlen;i<=slen;i++){
bool flag =true;
for (int j=1;j<=tlen;j++){
if (s[i-j+1]!='?'&&s[i-j+1]!=t[tlen-j+1]){
flag=false;
break;
}
}
g[i]=g[i-1];
if (flag){
f[i]=g[i-tlen];
for (int j=tlen;j;j=nxt[j]){
f[i]=max(f[i],f[i-(tlen-j)]);
}
f[i]++;
g[i]=max(g[i],f[i]);
}
}
// for (int i=tlen;i<=slen;i++){
// cout<<"g["<<i<<"]="<<g[i]<<endl;
// }
cout<<g[slen]<<endl;
return 0;
}