[Luogu P3167] [BZOJ 3507] [CQOI2014]萬用字元匹配
阿新 • • 發佈:2018-11-25
洛谷傳送門
BZOJ傳送門
題目描述
幾乎所有作業系統的命令列介面(CLI)中都支援檔名的萬用字元匹配以方便使用者。最常見的萬用字元有兩個,一個是星號(*
),可以匹配
個及以上的任意字元:另一個是問號(?
),可以匹配恰好一個任意字元。現在需要你編寫一個程式,對於給定的檔名列表和一個包含萬用字元的字串,判斷哪些檔案可以被匹配。
輸入輸出格式
輸入格式:
第一行是一個由小寫字母和上述萬用字元組成的字串。第二行包含一個整數 ,表示檔案個數。接下來 行,每行為一個僅包含小寫字母字串,表示檔名列表。
輸出格式:
輸出
行,每行為YES
NO
,表示對應檔案能否被萬用字元匹配。
輸入輸出樣例
輸入樣例#1:
*aca?ctc
6
acaacatctc
acatctc
aacacatctc
aggggcaacacctc
aggggcaacatctc
aggggcaacctct
輸出樣例#1:
YES
YES
YES
YES
YES
NO
說明
對於
的資料
- 字串長度不超過
- 萬用字元個數不超過
解題分析
貌似這玩意叫做正則表示式? 直接資瓷匹配?
我們可以發現實際上比較影響我們匹配的就是*
, 因為它會打亂我們的匹配位置, 所以直接在每個*
的位置斷開, 再維護每段內有幾段英文字母以及其雜湊值, 然後貪心找每段最早能在哪裡匹配到就可以了。
如果*
在最後面和最前面需要特判,我們可以直接在模板串和匹配串最前面和最後面加入A
, 就可以不用特判了。
細節比較多, 碼的時候要格外注意邊界問題。
程式碼如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define ll long long
#define BASE 233ll
#define MOD 19420817ll
#define gc getchar()
#define MX 100500
char mod[MX], buf[MX];
int tot, lena, lenb, seg;
struct INFO
{
int cnt, totlen;
int len[15], start[15];
ll key[15];
}dat[15];
int from[15], to[15], pos[15];
int n;
ll mul[MX], val[MX];
IN bool ismatch(R int ps, R int id)
{
ll hs1;
for (R int i = 1; i <= dat[id].cnt; ++i)
{
hs1 = (val[ps + dat[id].start[i] + dat[id].len[i] - 1] - val[ps + dat[id].start[i] - 1] * mul[dat[id].len[i]] % MOD + MOD) % MOD;
if (hs1 != dat[id].key[i]) return false;
}
return true;
}
int main(void)
{
ll hs;
int pre, cur = 1, bd;
scanf("%s", mod + 2);
lena = std::strlen(mod + 2); lena++;
mod[1] = 'A';
pos[0] = 0; mod[++lena] = 'A';
for (R int i = 1; i < lena; ++i) if (mod[i] == '*')
pos[++tot] = i, dat[tot].totlen = pos[tot] - pos[tot - 1] - 1;
pos[++tot] = lena + 1; dat[tot].totlen = lena - pos[tot - 1];
W (cur <= lena)
{
W (!isalpha(mod[cur])) ++cur;
from[++seg] = cur;
W (isalpha(mod[cur])) ++cur;
to[seg] = cur - 1;
}
cur = 1;
for (R int i = 1; i <= tot; ++i)
{
W (to[cur] < pos[i] && cur <= seg)
{
++dat[i].cnt;
dat[i].len[dat[i].cnt] = to[cur] - from[cur] + 1;
dat[i].start[dat[i].cnt] = from[cur] - pos[i - 1] - 1;
hs = 0;
for (R int i = from[cur]; i <= to[cur]; ++i) hs = (hs * BASE % MOD + mod[i]) % MOD;
dat[i].key[dat[i].cnt] = hs;
++cur;
}
}
mul[0] = 1;
for (R int i = 1; i <= 100000; ++i) mul[i] = mul[i - 1] * BASE % MOD;
scanf("%d", &n);
nx: W (n--)
{
scanf("%s", buf + 2); lenb = std::strlen(buf + 2); ++lenb;
buf[1] = buf[++lenb] = 'A';
val[1] = buf[1];
for (R int i = 2; i <= lenb; ++i) val[i] = (val[i - 1] * BASE % MOD + buf[i]) % MOD;
if (!ismatch(1, 1)) {puts("NO"); continue;}
cur = dat[1].totlen + 1;
for (R int i = 2; i < tot; ++i)//特殊判斷第一段和最後一段
{
if (!dat[i].cnt)
{
cur += dat[i].totlen;
continue;
}
bd = lenb - dat[i].totlen + 1;
for (; cur <= bd; ++cur)
{
if (ismatch(cur, i))
{
cur += dat[i].totlen;
goto st;
}
}
puts("NO"); goto nx;
st: ;
}
if (cur <= lenb - dat[tot].totlen + 1)
{
if (ismatch(lenb - dat[tot].totlen + 1, tot)) puts("YES");
else puts("NO");
}
else puts("NO");
}
}