[BZOJ3507]通配符匹配

3507: [Cqoi2014]通配符匹配

Time Limit: 10 Sec  Memory Limit: 128 MB

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

HINT

对于1 00%的数据
  ·字符串长度不超过1 00000
  ·  1 <=n<=100
  ·通配符个数不超过10

 

题解:

考场打暴力打炸了……

不过在后来改题的时候我发现了暴力思路的一个大问题……我们还是直接讲正解吧

题目给的通配符有2种,一种是’*’,它可以伸展;一种是‘?’,它的长度固定;

不难发现,给我们的处理带来难度的是'*',因为他的长度不确定;

但转念一想,如果除了*,其他的都能匹配,那*也就能匹配了.

注意到通配符个数<=10,那么我们可以先按照*,把原串分成一些包含'?'和字母的段.

而对于每一段,只要其中的所有小字符串成功匹配,并且符合'?'对长度的要求,这一段就匹配成功了

所以我们再在每一段里,按照'?'再分为几小块只含字母的串,并且分别求出hash值;同时求出文本串的hash值.

在匹配的时候,对于某一段,枚举起点,看在这一段在哪里可以匹配.

由于对于段间的枚举,影响因素只有*,所以匹配点肯定越靠前越好,因此这里可以贪心,即找到最靠前的匹配点即可停止.

而对于段内比较,利用刚才求的hash值比较就好.

代码见下:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<string>
 5 using namespace std;
 6 typedef unsigned long long LL;
 7 const int N=100010;
 8 const int mod1=9901;
 9 int n,lens,len,cnt,cnt1[20];
10 char s[N],text[N];
11 LL block[20][20],h[N],mi[N];
12 int l[20][20],num[20];
13 inline LL g_hash(int l,int r){return h[r]-h[l-1]*mi[r-l+1];}
14 inline bool match(int i,int k)
15 {
16     for(int j=0;j<=num[k];j++)
17     {
18         if(l[k][j]==0){i++;continue;}
19         if(block[k][j]!=g_hash(i,i+l[k][j]-1))return 0;
20         i+=l[k][j]+1;
21     }
22     return 1;
23 }
24 bool judge()
25 {
26     if(!cnt)
27     {
28         if(len!=lens)return 0;
29         for(int i=1;i<=len;i++)
30             if(s[i]!=text[i]&&s[i]!='?')return 0;
31         return 1;
32     }
33     if(len<(cnt1[1]-1)+(lens-cnt1[cnt]))return 0;
34     for(int i=1;i<cnt1[1];i++)if(s[i]!=text[i]&&s[i]!='?')return 0;
35     for(int i=lens,j=len;i>cnt1[cnt];i--,j--)if(s[i]!=text[j]&&s[i]!='?')return 0;
36     for(int i=1;i<=len;i++)
37         h[i]=h[i-1]*mod1+text[i];
38     int i=cnt1[1],line=len-(lens-cnt1[cnt])+1;
39     for(int k=2;k<=cnt;k++)
40     {
41         for(;i<=line;i++)
42             if(match(i,k))break;
43         i+=cnt1[k]-cnt1[k-1]-1;
44         if(i>line)return 0;
45     }
46     return 1;
47 }
48 int main()
49 {
50     scanf("%s",s+1);lens=strlen(s+1);
51     mi[0]=1;for(int i=1;i<=lens;i++)mi[i]=mi[i-1]*mod1;
52     for(int i=1;i<=lens;i++)
53         if(s[i]=='*')cnt1[++cnt]=i;
54     for(int i=2;i<=cnt;i++)
55     {
56         num[i]=0;
57         for(int j=cnt1[i-1]+1;j<cnt1[i];j++)
58         {
59             if(s[j]=='?'){num[i]++;continue;}
60             l[i][num[i]]++;
61             block[i][num[i]]=block[i][num[i]]*mod1+s[j];
62         }
63     }
64     scanf("%d",&n);
65     for(int i=1;i<=n;i++)
66     {
67         scanf("%s",text+1);len=strlen(text+1);
68         if(judge())printf("YES\n");
69         else printf("NO\n");
70     }
71 }
BZOJ3507
posted @ 2017-06-18 17:08  LadyLex  阅读(427)  评论(0编辑  收藏  举报