我们将题目输入的那个含通配符的串,记为“通配符串”,下面的文件名记为“文件名”,“文件名”数量很少可以依次查询。
我们先将“通配符串”以‘*’为界,将“通配符串”分解,得到若干子串,记为“通配子串”
我们将每个“通配子串”各建立一个AC自动机。
而有的“通配子串”可能含‘?’通配符,我们将再次按‘?’为界,分解这个“通配子串”,得到的子串记为“划分子串”,然后将“划分子串”加入该“通配子串”所在的AC自动机上(原“通配子串”就不加进去了)。
匹配“文件名”时,我们应按“通配子串”的顺序依次匹配“文件名”,检查该“文件名”是否可行,靠前的“通配子串”先匹配,如果某次匹配不成功,该“文件名”不可以匹配。特殊的,对于含‘?’的“通配子串”,我们可以按下图方式匹配。
还有些细节就是对于两个通配符串"*ab?c"和"ab?c",第二个通配符串的a前面不允许有其它字符,我的做法是在通配符串的前后加上'{',也就是得到了"{*ab?c{",读取“文件名”也这做,就可以解决。
另一个细节就是出现通配符子串"abc??"时,要求文件名的一段"abc"的后面还有两个字符,我的做法是:视"??"后面为空串,加入AC自动机(这个也可以特判一下,我的做法可以看下代码> <,但是我的代码很丑啊)。
最后提一个细节就是:对于通配符子串可能有多个相同的划分子串,比如"abc?abc”。然后你就知道该怎么做了。
剩下的细节就不必讨论了。
#include<bits/stdc++.h>
using namespace std;
#define idx(c) ((c) - 'a')
const int MAX_NODE = 100000 + 10;
const int MAX_SIZE = 27;
const char ocu = '{';
int ch[MAX_NODE + 10][MAX_SIZE], sz = 1;
vector<int> val[MAX_NODE + 10]; //vector类型的结点标记
int f[MAX_NODE + 10], last[MAX_NODE + 10];
int new_node() {
return sz++;
}
int cnt[MAX_NODE + 10];
struct trie {
int root, str_cnt;
trie() { root = new_node(); str_cnt = 0; }
void ins(const string &s, int v) {
int i, u = root;
for(i = 0; i < s.length(); i++) {
int c = idx(s[i]);
if(ch[u][c] == 0)
ch[u][c] = new_node();
u = ch[u][c];
}
val[u].push_back(v);
str_cnt++;
}
void get_fail() {
queue<int> q;
f[root] = root; last[root] = root;
for(int i = 0; i < MAX_SIZE; i++) {
int &r = ch[root][i];
if(r == 0) r = root;
else {
q.push(r);
f[r] = last[r] = root;
}
}
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = 0; i < MAX_SIZE; i++) {
int &r = ch[u][i];
if(r == 0) { r = ch[f[u]][i]; continue; }
q.push(r);
f[r] = ch[f[u]][i];
last[r] = val[f[r]].size() ? f[r] : last[f[r]];
}
}
}
bool query(const string &s, int st, int &ed) {
memset(cnt, 0, sizeof(cnt));
int u = root;
for(int i = st; i < s.length(); i++) {
u = ch[u][idx(s[i])];
int tmp = u;
for(int j = 0; j < val[root].size(); j++) {
int k = i - val[root][j] + 1;
if(k >= 0) {
cnt[k]++;
if(cnt[k] == str_cnt) return ed = i, 1;
}
}
while(tmp != root) {
for(int j = 0; j < val[tmp].size(); j++) {
int k = i - val[tmp][j] + 1;
if(k >= 0) {
cnt[k]++;
if(cnt[k] == str_cnt) return ed = i, 1;
}
}
tmp = last[tmp];
}
}
return 0;
}
};
vector<trie> A; //我将每个AC自动机存在A里面
bool match(const string &s) { //匹配文件名
int i, next_st = 0, next_ed;
for(i = 0; i < A.size(); i++) { //依次匹配AC自动机
bool ok = A[i].query(s, next_st, next_ed);
if(ok == 0) break;
next_st = next_ed + 1;
}
return i == A.size();
}
trie split(const string &s) { //将划分子串加入AC自动机
trie res;
string tmp;
for(int i = 0; ; i++) {
if(i < s.length() && s[i] != '?') tmp.push_back(s[i]);
else {
if(i != 0) res.ins(tmp, i); //实现时我将认为任意一个'?'后都可以补上空串
tmp.clear();
}
if(i == s.length()) break;
}
res.get_fail();
return res;
}
void prepare(const string &s) { //分离出通配符子串然后依次建立AC自动机
string tmp;
for(int i = 0; i < s.length(); ) {
while(i < s.length() && s[i] != '*')
tmp.push_back(s[i++]);
A.push_back(split(tmp));
while(i < s.length() && s[i] == '*')
i++;
tmp.clear();
}
}
char R[MAX_NODE + 10] = "{";
string buf;
int main() {
scanf("%s", R + 1);
buf = string(R);
buf.push_back('{');
prepare(buf);
int i, n;
scanf("%d", &n);
for(i = 1; i <= n; i++) {
scanf("%s", R + 1);
buf = string(R);
buf.push_back('{');
if(match(buf)) puts("YES");
else puts("NO");
}
return 0;
}