#10057. 「一本通 2.4 例 1」Keywords Search

给定 个长度不超过 的由小写英文字母组成的单词准备查询,以及一篇长为 的文章,问:文中出现了多少个待查询的单词。多组数据。
输入格式
第一行一个整数 ,表示数据组数;
对于每组数据,第一行一个整数 ,接下去 行表示 个单词,最后一行输入一个字符串,表示文章。
输出格式
对于每组数据,输出一个数,表示文中出现了多少个待查询的单词。
样例输入

1
5
she
he
say
shr
her
yasherhs

样例输出

3

数据范围与提示
对于全部数据 1<=n<=1e4 1<=m<=1e6
注意清空数据 直接在主函数里面清空就行了 简单明了
关于什么是ac自动机 这位大佬讲的很明了 点这里

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#include <queue>;
const int N = 2e6 + 10;
char s[N];
int tot = 0;    //记录结点
int bo[N];      //记录单词出现次数
int fail[N];    //失败时的回溯指针
int ch[N][26];  //字典数
void insert() {
    int l = strlen(s), u = 0;
    for (int i = 0; i < l; i++) {
        int c = s[i] - 'a';
        if (!ch[u][c]) {
            ch[u][c] = ++tot;
            //  memset(ch[tot],0,sizeof(ch));
        }

        u = ch[u][c];
    }
    bo[u]++;  // u表示字符串最后一个字符
}
void bfs() {
    queue<int> q;
    for (int i = 0; i < 26; i++)  //将第二层出现了的字符放进队列
    {
        if (ch[0][i]) {
            fail[ch[0][i]] = 0;  //如果根节点的下一个节点存在 则将他的fail指针指向根节点
            q.push(ch[0][i]);
        }
    }
    // now  当前节点
    // ch[now][i]   当前节点的子结点
    while (!q.empty()) {
        int now = q.front();
        q.pop();
        for (int i = 0; i < 26; i++)  //查询26个字母
        {
            if (ch[now]
                  [i])  //如果子结点存在
                        //就让子结点的失败指针指向当前节点的失败指针指向的节点的下一个节点,个人认为这样好理解
            {
                fail[ch[now][i]] = ch[fail[now]][i];
                q.push(ch[now][i]);
            } else  //如果不存在就让当前节点的子结点指向当前节点fail指针指向的节点
                ch[now][i] = ch[fail[now]][i];
        }
    }
}
int find() {
    int u = 0, ans = 0;
    int l = strlen(s);
    for (int i = 0; i < l; i++) {
        int c = s[i] - 'a';
        u = ch[u][c];
        for (int j = u; j && bo[j] != -1;
             j = fail[j])  //一直到失败指针指向根节点或者当前节点已经找过,才会停止寻找
        {
            ans += bo[j];
            bo[j] = -1;
        }
    }
    return ans;
}
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        memset(bo, 0, sizeof(bo));
        memset(ch, 0, sizeof(ch)); //这里要清空 不然下组数据会超时
        int n;
        scanf("%d", &n);
        while (n--) {
            scanf("%s", s);
            insert();
        }
        fail[0] = 0;
        bfs();
        scanf("%s", s);
        printf("%d\n", find());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值