bzoj3507 [Cqoi2014]通配符匹配

我们将题目输入的那个含通配符的串,记为通配符串,下面的文件名记为文件名文件名数量很少可以依次查询。

我们先将“通配符串”以‘*’为界,将通配符串分解,得到若干子串,记为通配子串

我们将每个“通配子串”各建立一个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;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值