Codeforces Educational Round#21 G Solution:双函数DP+KMP

本文介绍了一种利用动态规划解决带有通配符的字符串匹配问题的方法。通过定义两个辅助数组f和g来分别记录匹配状态和最大匹配次数,结合KMP算法预处理目标字符串,实现了高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目意思是给出一个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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值