HDU6096 ac自动机+fail树

题目问每个”prefix + ’ ’ + suffix”类型的串能匹配上多少个串S。
可以做处理将”prefix + ’ ’ + suffix” 转化为 “suffix + ‘#’ + prefix”类型的串,然后把所有S串扩充成”S + # + S”串。
问题就转化成了,给定m个串,问每个串是多少个s串的子串。
首先不考虑前缀后缀不允许重合的条件。
将m个串建ac自动机,分别用n个串去匹配,遇到end节点就+1即可,需要注意的是一个节点的fail是end节点,那么这个节点也是end节点(fail树的性质)。
之后建立fail树,之后dfs跑一边树上前缀和即可(或者不建树,直接bfs跑一遍)。
现在考虑前缀后缀不允许重合的条件,本质上也就是限制S串匹配的深度,若该S串的长度为len,则当前节点为危险节点时且深度小于等于len+1(我们给S串添加了‘#’字符)时,必然满足条件,因为此时的”prefix + suffix”的长度只会小于等于len。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
vector<int> G[550000];
struct Ac{
    static const int maxm = 5500000;
    static const int maxn = 30;
    int next[maxm][maxn], pos[maxm], fail[maxm], cnt[maxm], deep[maxm], d[maxm], end[maxm];
    int root, L;
    int getid(char c) {
        if(islower(c))
            return c-'a';
        if(c == '#') return 26;
    }
    int newnode() {
        for(int i = 0; i < maxn; i++) next[L][i] = -1;
        d[L] = cnt[L] = end[L] = fail[L] = 0;
        pos[L++] = 0;
        return L - 1;
    }
    void init() {
        L = 0;
        root = newnode();
    }
    void insert(string s, int ss) {
        int len = s.size();
        int p = root;
        deep[p] = 0;
        for(int i = 0; i < len; i++) {
            int id = getid(s[i]);
            if(next[p][id] == -1)
                next[p][id] = newnode();
            int v = deep[p];
            p = next[p][id];
            deep[p] = v + 1;
        }
        pos[ss] = p;
        end[p] = 1;
    }
    void build() {
        queue<int> q;
        int p = root;
        for(int i = 0; i < 27; i++) {
            if(next[p][i] == -1) {
                next[p][i] = root;
            } else {
                fail[next[p][i]] = p;
                q.push(next[p][i]);
            }
        }
        while(!q.empty()) {
            int now = q.front();
            q.pop();
            for(int i = 0; i < 27; i++) {
                if(next[now][i] == -1) next[now][i] = next[fail[now]][i];
                else {
                    fail[next[now][i]] = next[fail[now]][i];
                    q.push(next[now][i]);
                }
            }
        }
        for(int i = 0; i < L; i++)
            if(end[fail[i]]) end[i] = 1;
    }
    void ask(string s, int h) {
        int len = s.size();
        int now = root;
        for(int i = 0; i < len; i++) {
            int v = getid(s[i]);
            now = next[now][v];
            while(deep[now] > h)
                now = fail[now];
            if(end[now]) cnt[now]++;
        }
    }
    void getans() {
        queue<int> q;
        for(int i = 0; i < L; i++) d[fail[i]] ++;
        for(int i = 0; i < L; i++) if(d[i] == 0) q.push(i);
        while(!q.empty()) {
            int v = q.front();
            q.pop();
            cnt[fail[v]] += cnt[v];
            v = fail[v];
            d[v]--;
            if(d[v] == 0) q.push(v);
        }
    }
}ac;


int t, n, q;
string str[550000];
int len[550000];

int main(){
//    freopen("1001.in", "r", stdin);
//    freopen("out.txt", "w", stdout);
    cin.tie(0);
    ios::sync_with_stdio(0);
    cin >> t;
    while(t--) {
        cin >> n >> q;
        string tmp;
        for(int i = 1; i <= n; i++) {
            cin >> tmp;
            str[i].clear();
            str[i] += tmp;
            str[i] += '#';
            len[i] = tmp.size() + 1;
            str[i] += tmp;
        }
//        ss = str;
        ac.init();
        for(int i = 1; i <= q; i++) {
            string s1, s2;
            cin >> s1 >> s2;
            tmp = s2;
            tmp += '#';
            tmp += s1;
            ac.insert(tmp, i);
        }
        ac.build();
        for(int i = 1; i <= n; i++)
            ac.ask(str[i], len[i]);
        ac.getans();
        for(int i = 1; i <= q; i++)
            cout<<ac.cnt[ac.pos[i]]<<'\n';
        for(int i = 0; i < ac.L; i++)G[i].clear();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值