P3167 通配符匹配(字符串哈希+dp)

题目链接:

​​​​​​P3167 [CQOI2014]通配符匹配 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目大意:

给出一个主串,里面'*'可以匹配0个及其以上的任意字符串,另一个是问号'?',可以匹配任意一个字符,现在有一堆匹配串,求每一个是否能和主串匹配?

思路:

因为*涉及到匹配多少个的字符问题,加上又是字符串匹配,所以可以想到dp,然后分析一下时间复杂度,如果f[i][j]表示主串前i个字符和匹配串前j个字符串匹配的话,时间复杂度会超时,而且用哈希在判断是否匹配的时候,还要特别注意通配符的位置。

所以就思考能否设置为前i个通配符和前j个字符匹配,这样就可以直接通过哈希判断了字符匹配了,时间复杂度是 通配符数量 * 字符串的长度 * 字符串的个数,刚好1e8不会超时。

因为第一维是遍历通配符,如果字符串里面一个通配符都没有,就不会进行匹配,所以我们在主串的结尾+?这样就可以确保主串一定会匹配到末尾,因为要匹配,所以匹配串结尾对应应该也加一个可以和?匹配的字符,和?匹配的随便选一个就可以了。

在遍历通配符的过程中,如果通配符是*那么如果开头可以匹配,所有后面的主串都可以和*匹配。

然后就是匹配两个通配符之间的字符,注意因为如果后面的通配符是?,因为?必须匹配一个字符,所以他们不仅匹配到中间的字符,还匹配到右端点字符。

#include <algorithm>
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
typedef unsigned long long ull;
const int N = 100010, P = 131;
int n, m, k, tpf[15], cnt = 0, ans;
ull h1[N], h2[N], p[N];
string s, str;
int f[15][N];
int main()
{
    cin >> s;
    s = " " + s + "?";
    n = s.size() - 1;
    p[0] = 1;
    for (int i = 1; i < N; i++)
        p[i] = p[i - 1] * P;

    for (int i = 1; i <= n; i++)
    {
        h1[i] = h1[i - 1] * P + s[i];
        if (s[i] == '*' || s[i] == '?')
            tpf[++cnt] = i;
    }

    scanf("%d", &k);
    while (k--)
    {
        cin >> str;
        str = " " + str + "a";
        m = str.size() - 1;
        for (int i = 1; i <= m; i++)
            h2[i] = h2[i - 1] * P + str[i];

        // f表示,前i个通配符和前j个字符是否能匹配
        memset(f, 0, sizeof f);
        f[0][0] = 1;
        for (int i = 0; i <= cnt; i++)
        {
            if (s[tpf[i]] == '*')
                for (int j = 1; j <= m; j++)
                    if (f[i][j - 1])
                        f[i][j] = 1;
            //匹配下一个
            for (int j = 0; j <= m; j++)
            {
                //如果当前已经不匹配,下一堆一定不匹配
                if (!f[i][j])
                    continue;
                int lt = tpf[i] + 1, rt = tpf[i + 1] - 1;
                int len = rt - lt + 1;
                int ls = j + 1, rs = j + len;
                if (h1[rt] - h1[lt - 1] * p[len] == h2[rs] - h2[ls - 1] * p[len])
                    f[i + 1][rs + (s[tpf[i + 1]] == '?')] = 1;
            }
        }
        printf(f[cnt][m] ? "YES\n" : "NO\n");
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值