Consistent Occurrences(AC自动机)

题目链接:传送门

题意分析:

       先给你一个长度为n的模式串,然后给你m个匹配串,让你求出m个匹配串中每个匹配串在模式串中不重叠的个数。

题目分析:

       如果只是一个匹配串,那当然就用KMP了,多个匹配串,这不就是AC自动机的模板嘛。但有一个问题,就是不能重叠。解决这个问题很简单,把结果开成2维的,如ans[m][2], 其中ans[i][0]表示上第i个匹配串的答案,ans[i][1]表示第i个匹配串表示上一个记录答案的位置,那样的话,就可以通过(i - ans[d][1]+1) >= le[d]特判是否有贡献,其中i表示模式串的位置,d表示第几个匹配串。还有注意的是,因为匹配串可以相同的,所以要用到哈希处理一下。

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
string s, str;
char te[maxn];
int ans[maxn][3];
map<string, int>ma;
int ha[maxn];
int le[maxn];
struct node
{
    int cnt;
    node *next[26], *fail;
    node(){
        cnt = 0;
        fail = NULL;
        memset(next, NULL, sizeof(next));
    }
}*q[maxn], *root;

void insert1(char* s, node *p, int d)
{
    int i = 0, k;
    p = root;
    while(s[i]){
        k = s[i++]-'a';
        if(p->next[k] == NULL) p->next[k] = new node();
        p = p->next[k];
    }
    p->cnt = d;
}

void build(node *root)
{
    int i, head=0, tail=1;
    q[head] = root;
    node *p, *temp;
    while(head < tail){
        temp = q[head++];
        for(i=0;i<26;++i){
            if(temp->next[i]){
                if(temp == root) temp->next[i]->fail = root;
                else{
                    p = temp->fail;
                    while(p){
                        if(p->next[i]) {
                            temp->next[i]->fail = p->next[i];
                            break;
                        }
                        p = p->fail;
                    }
                    if(!p) temp->next[i]->fail = root;
                }
                q[tail++] = temp->next[i];
            }
        }
    }
}

void query(char* s, node *root)
{
    int  k, len = strlen(s);
    node *p = root;
    for(int i=0;i<len;++i){
        k = s[i]-'a';
        while(!p->next[k] && p != root) p = p->fail;
        p = p->next[k];
        if(!p) p = root;
        node *temp = p;
        while(temp != root){
            int d = temp->cnt;
            if( (i - ans[d][1]+1) >= le[d]) {
                ans[d][0]++;
                ans[d][1] = i+1;
            }
            temp = temp->fail;
        }
    }
}
int main()
{
    int t, n;
    memset(ans, 0, sizeof(ans));
    scanf("%d %d", &t, &n);
    cin >> s;
    root = new node;
    for(int i=1;i<=n;++i){
        cin >> str;
        le[i] = str.length();
        if(ma[str]) ha[i] = ma[str];
        else{
            ma[str] = i;
            ha[i] = i;
            insert1((char*)str.c_str(), root, i);
        }
    }
    build(root);
    query((char*)s.c_str(), root);
    for(int i=1;i<=n;++i){
        cout << ans[ha[i]][0] << endl;
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值