410 字
2 分钟
BZOJ 3507 [Cqoi2014]通配符匹配 hash 题解
2017-06-18

Description#

几乎所有操作系统的命令行界面(CLI)中都支持文件名的通配符匹配以方便用户。最常见的通配符有两个,一个 是星号(“”’),可以匹配0个及以上的任意字符:另一个是问号(“?”),可以匹配恰好一个任意字符。 现在需要你编写一个程序,对于给定的文件名列表和一个包含通配符的字符串,判断哪些文件可以被匹配。

Input#

第一行是一个由小写字母和上述通配符组成的字符串。
第二行包含一个整数n,表示文件个数。
接下来n行,每行为一个仅包含小写字母字符串,表示文件名列表。

Output#

输出n行,每行为“YES”或“NO”,表示对应文件能否被通配符匹配。

Sample Input#

*aca?ctc
6
acaacatctc
acatctc
aacacatctc
aggggcaacacctc
aggggcaacatctc
aggggcaacctct

Sample Output#

YES
YES
YES
YES
YES
NO

题解#

将原字符串由’*‘分成若干段
记录每一个’?‘的位置
分段hash 将文件首尾先匹配然后在中间贪心的匹配

/*
 * @Author: WildRage 
 * @Date: 2017-06-17 10:45:09 
 * @Last Modified by: WildRage
 * @Last Modified time: 2017-06-17 11:09:39
 */
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
#define ULL unsigned long long
const ULL base=31; 
ULL pw[100005],f[15][15],HASH[100005];
int pos[15],cnt,num[15],s[15][15],n,lenth;
char S[100005],a[100005];
ULL get_hash(int l,int r){
    return HASH[r]-HASH[l-1]*pw[r-l+1];
}
bool OK(int i,int k){
    for(int j=0;j<=num[k];j++){
        if(s[k][j]==0){i++;continue;}
        if(f[k][j]!=get_hash(i,i+s[k][j]-1))return 0;
        i+=s[k][j]+1;
    }
    return 1;
}
bool work(char *b){
    int len=strlen(b+1);
    if(!cnt){
        if(lenth!=len)return 0;
        for(int i=1;i<=lenth;i++)
            if(S[i]!=b[i]&&S[i]!='?')return 0;
        return 1;
    }
    if(len<(pos[1]-1)+(lenth-pos[cnt]))return 0;
    for(int i=1;i<pos[1];i++)if(S[i]!=b[i]&&S[i]!='?')return 0;
    for(int i=lenth,j=len;i>pos[cnt];i--,j--)
        if(S[i]!=b[j]&&S[i]!='?')return 0;
    for(int i=1;i<=len;i++) HASH[i]=HASH[i-1]*base+b[i];
    int MAX=len-(lenth-pos[cnt])+1;
    int i=pos[1];
    for(int k=2;k<=cnt;k++){
        while(i<=MAX){
            if(OK(i,k))break;
            i++;
        }
        i+=pos[k]-pos[k-1]-1;
        if(i>MAX) return 0;
    }
    return 1;
}
int main()
{
    //freopen("match.in","r",stdin);
    //freopen("match.out","w",stdout);
    scanf("%s",S+1);
    lenth=strlen(S+1);
    pw[0]=1;
    cnt=0;
    for(int i=1;i<=100000;i++)pw[i]=pw[i-1]*base;
    for(int i=1;i<=lenth;i++)
        if(S[i]=='*') 
            pos[++cnt]=i;
    for(int i=2;i<=cnt;i++){
        num[i]=0;
        for(int j=pos[i-1]+1;j<pos[i];j++){
            if(S[j]=='?'){
                num[i]++;
                continue;
            }
            s[i][num[i]]++;
            f[i][num[i]]=f[i][num[i]]*base+S[j];
        }
    }
    scanf("%d",&n);
    while(n--){
        scanf("%s",a+1);
        if(work(a))puts("YES");
        else puts("NO");
    }
    //while(1);
}
BZOJ 3507 [Cqoi2014]通配符匹配 hash 题解
https://www.nekomio.com/posts/15/
作者
NekoMio
发布于
2017-06-18
许可协议
CC BY-NC-SA 4.0