String —— AC自动机(二)

原题链接:HDU-6096

题意简述

给出 n 个字符串,再给出 q 个查询,每次查询读入两个字符串 p,s,求在 n 个字符串中以 p 为前缀且以 s 为后缀的字符串有多少个

思路分析

考虑 AC 自动机,其本质是求 n 个模式串在 1 个文本串中出现的次数,我们将每次查询时读入的 p,s 以 s + ‘#’ + p 的格式构建新的字符串,然后建立 trie 树,记录结点位置。然后将 n 个字符串构建成 str + ‘#’ + str 的新字符串作为文本串跑 AC 自动机,每匹配到一个结点,结点处计数器加 1 。

为了避免 aa 和 aa 匹配到 aaa 的情况,在结点处标记上长度 len,只有当大于等于 len 时,才计数

源代码:

#include
#include
#include
#include
#include
using namespace std;

const int maxn = 2e6 + 10;


struct f
{
    int son[27], fail;
}trie[maxn];
int n, m, cnt;
int len[maxn], ans[maxn];
string p[maxn];
char a[maxn];
char b[maxn];
char temp[maxn];
int pos[maxn];

void clear()
{
    for (int i = 0; i <= cnt; ++i)
    {
        memset(trie[i].son, 0, sizeof(trie[i].son));
        trie[i].fail = 0;
    }
    memset(len, 0, sizeof(len));
    memset(ans, 0, sizeof(ans));
}

int insert(char *s)
{
    int u = 1;
    for (int i = 1; s[i]; ++i)
    {
        int v = s[i]-'a';
        if (!trie[u].son[v])
        {
            trie[u].son[v] = ++cnt;
            len[cnt] = i;
        }
        u = trie[u].son[v];
    }
    return u;
}

void getfail()
{
    for (int i = 0; i < 27; ++i)
        trie[0].son[i] = 1;
    trie[1].fail = 0;
    queue<int> q;
    q.push(1);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        int Fail = trie[u].fail;
        for (int i = 0; i < 27; ++i)
        {
            int v = trie[u].son[i];
            if (!v)
            {
                trie[u].son[i] = trie[Fail].son[i];
                continue;
            }
            trie[v].fail = trie[Fail].son[i];
            q.push(v);
        }
    }
}

void query(char *s, int l)
{
    int u = 1;
    for (int i = 1; s[i]; ++i)
    {
        int v = s[i]-'a';
        int k = trie[u].son[v];
        while (k > 1)
        {
            if (l >= len[k]) ++ans[k];
            k = trie[k].fail;
        }
        u = trie[u].son[v];
    }
}

int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        cnt = 1;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; ++i)
            cin >> p[i];
        for (int i = 1; i <= m; ++i)
        {
            scanf("%s%s", a+1, b+1);
            sprintf(temp+1, "%s%c%s", b+1, 'z'+1, a+1);
            pos[i] = insert(temp);
        }
        getfail();
        for (int i = 1; i <= n; ++i)
        {
            sprintf(temp+1, "%s%c%s", p[i].c_str(), 'z'+1, p[i].c_str());
            query(temp, p[i].size()+1);
        }
        for (int i = 1; i <= m; ++i)
            printf("%d\n", ans[pos[i]]);
        clear();
    }
    return 0;
}

你可能感兴趣的:(String —— AC自动机(二))