Description
几乎所有操作系统的命令行界面(CLI)中都支持文件名的通配符匹配以方便用户。最常见的通配符有两个,一个
是星号(“”’),可以匹配0个及以上的任意字符:另一个是问号(“?”),可以匹配恰好一个任意字符。
现在需要你编写一个程序,对于给定的文件名列表和一个包含通配符的字符串,判断哪些文件可以被匹配。
Input
第一行是一个由小写字母和上述通配符组成的字符串。
第二行包含一个整数n,表示文件个数。
接下来n行,每行为一个仅包含小写字母字符串,表示文件名列表。
Output
输出n行,每行为“YES”或“NO”,表示对应文件能否被通配符匹配。
题解:
可以发现唯一存在影响的只有通配符’*‘和’?’,而‘?'的影响也不大,稍微判断一下就可以,所以唯一需要考虑的是‘ *’,考虑设dp[i][j]表示已经匹配了i个通配符,下面的字符串匹配到了第j个字符的方案是否存在。
详解在代码中↓
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
#define ull unsigned long long
#define pii pair<int,int>
#define mp(a,b) make_pair(a,b)
const int MAXN = 1e5+10;
const int BASE = 131;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
char ch[MAXN],s[MAXN];
ull hash1[MAXN],hash2[MAXN],pw[MAXN];
int pos[50],tot,n,m; bool dp[20][MAXN];
signed main(){
#ifndef ONLINE_JUDGE
freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
#endif // ONLINE_JUDGE
scanf("%s",s+1); n=strlen(s+1)+1; s[n]='?';
pw[0]=1; for(int i=1;i<=1e5;i++) pw[i]=pw[i-1]*BASE;
for(int i=1;i<=n;i++) hash1[i]=hash1[i-1]*BASE+s[i];
for(int i=1;i<=n;i++) if(s[i]=='*' || s[i]=='?') pos[++tot]=i;
//提取通配符
int Q; scanf("%d",&Q);
while(Q--){
memset(dp,false,sizeof(dp)); dp[0][0]=true;
scanf("%s",ch+1); m=strlen(ch+1); ch[++m]='#';
for(int i=1;i<=m;i++)hash2[i]=hash2[i-1]*BASE+ch[i];
for(int i=0;i<=tot;i++){
//如果是'*',如果j这个位置的方案是存在的,那么之后的方案也可以通过'*'拓展而来
//所以只要有一个位置的方案存在,后面的方案也应该都存在
if(s[pos[i]]=='*')
for(int j=1;j<=m;j++) dp[i][j]|=dp[i][j-1];
for(int j=0;j<=m;j++){//枚举下面匹配到j位置了
if(!dp[i][j]) continue;
//提取区间判断相等
int l1=j+1,r1=j+(pos[i+1]-pos[i])-1;
int l2=pos[i]+1,r2=pos[i+1]-1;
if(hash2[r1]-hash2[l1-1]*pw[r1-l1+1]==hash1[r2]-hash1[l2-1]*pw[r2-l2+1]){
//两段子串可以匹配上,那么r1这个位置肯定是存在方案的
if(s[pos[i+1]]=='?') dp[i+1][r1+1]=true;
//如果下一个是'?',那么下一位字符也能匹配到
else dp[i+1][r1]=true;
}
}
}
puts(dp[tot][m] ? "YES":"NO");
}
return 0;
}