1. 程式人生 > >[Luogu P3167] [BZOJ 3507] [CQOI2014]萬用字元匹配

[Luogu P3167] [BZOJ 3507] [CQOI2014]萬用字元匹配

洛谷傳送門

BZOJ傳送門

題目描述

幾乎所有作業系統的命令列介面(CLI)中都支援檔名的萬用字元匹配以方便使用者。最常見的萬用字元有兩個,一個是星號(*),可以匹配 0 0 個及以上的任意字元:另一個是問號(?),可以匹配恰好一個任意字元。現在需要你編寫一個程式,對於給定的檔名列表和一個包含萬用字元的字串,判斷哪些檔案可以被匹配。

輸入輸出格式

輸入格式:

第一行是一個由小寫字母和上述萬用字元組成的字串。第二行包含一個整數 n

n ,表示檔案個數。接下來 n n 行,每行為一個僅包含小寫字母字串,表示檔名列表。

輸出格式:

輸出 n n 行,每行為YES

NO,表示對應檔案能否被萬用字元匹配。

輸入輸出樣例

輸入樣例#1:

*aca?ctc
6
acaacatctc
acatctc
aacacatctc
aggggcaacacctc
aggggcaacatctc
aggggcaacctct

輸出樣例#1:

YES
YES
YES
YES
YES
NO

說明

對於 100 % 100\%

的資料

  • 字串長度不超過 100000 100000
  • 1 n 100 1 \le n\le 100
  • 萬用字元個數不超過 10 10

解題分析

貌似這玩意叫做正則表示式? C + + 11 C++11 直接資瓷匹配?

我們可以發現實際上比較影響我們匹配的就是*, 因為它會打亂我們的匹配位置, 所以直接在每個*的位置斷開, 再維護每段內有幾段英文字母以及其雜湊值, 然後貪心找每段最早能在哪裡匹配到就可以了。

如果*在最後面和最前面需要特判,我們可以直接在模板串和匹配串最前面和最後面加入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");
	}
}