AC自动机+拓扑排序

题目

题目

思路

暴力解法:对于每个最长匹配,迭代跳fail,累计贡献。时间复杂度 O ( n 2 ) O(n^2) O(n2)

优化:只标记最长匹配,最后用拓扑排序计算贡献。时间复杂度 O ( n ) O(n) O(n)

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 2e6 + 5;
const int M = 2e5 + 5;

int tr[N][26];
char s[N];
int ans[M];
int out[M];
int fail[N];
int ct;
vector<int> id[N];

void insert(char s[], int x)
{
    int p = 0;
    for (int i = 1; s[i]; i++)
    {
        if (!tr[p][s[i] - 'a'])
            tr[p][s[i] - 'a'] = ++ct;
        p = tr[p][s[i] - 'a'];
    }
    id[p].emplace_back(x);
}
int vis[N];
void build()
{
    queue<int> q;
    for (int i = 0; i < 26; i++)
        if (tr[0][i])
            q.push(tr[0][i]);
    while (q.size())
    {
        int p = q.front();
        q.pop();
        for (int i = 0; i < 26; i++)
        {
            if (tr[p][i])
            {
                fail[tr[p][i]] = tr[fail[p]][i], q.push(tr[p][i]);
                vis[tr[fail[p]][i]]++;
            }
            else
            {
                tr[p][i] = tr[fail[p]][i];
            }
        }
    }
}

void get()
{
    int p = 0;
    for (int i = 1; s[i]; ++i)
    {
        p = tr[p][s[i] - 'a'];
        ans[p]++;
    }
}
bool fuck[N];
void topo()
{
    queue<int> q;
    for (int i = 1; i <= ct; i++)
    {
        if (!vis[i])
            q.push(i), fuck[i] = 1;
    }
    while (q.size())
    {
        int i = q.front();
        q.pop();
        vis[fail[i]]--;
        if (!vis[fail[i]])
            q.push(fail[i]), fuck[fail[i]] = 1;
        ans[fail[i]] += ans[i];
    }
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%s", s + 1);
        insert(s, i);
    }
    scanf("%s", s + 1);
    build();
    get();
    topo();
    for (int i = 1; i <= ct; i++)
    {
        if (id[i].size())
        {
            int temp = ans[i];
            for (auto &it : id[i])
                out[it] = temp;
        }
    }
    for (int i = 1; i <= n; i++)
        printf("%d\n", out[i]);
    return 0;
}

/*

*/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hesorchen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值