题目链接:
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;
}