1 条题解

  • 0
    @ 2026-10-26 13:00:02

    思路与算法

    把所有模式插入Trie并记录每个查询项的终止节点;重复字符串保留多个终止节点记录。BFS构造fail指针,扫描文章时沿Trie/失配边更新状态,对经过的状态计数。最后按BFS逆序把计数加到fail父节点,每个查询终止节点的计数>0即这项查询出现过,累加这些布尔值。采用首孩子/兄弟链的稀疏转移,降低内存。

    正确性说明

    扫描前缀后的状态表示其最长Trie后缀。一个模式在当前位置结束当且仅当其终止节点位于当前状态的fail链。逆BFS聚合将每次状态访问传给全部fail祖先,故每个终止节点的最终计数正好为该模式的出现次数。只判断是否大于0而不是直接累加次数,满足“出现了多少待查询单词”;相同查询项各自计一次。

    复杂度

    设模式总长S、文章长M、字母表σ=26,节点数V≤S+1。稀疏边查找最多σ步,插入、构造及扫描的上界可按各模式路径和文章上的失配摊还为O(σ(S+M)),BFS聚合O(V),初始化节点与读取O(S+M)。空间O(S+M+n),节点按实际总长预留,每节点约32字节及4字节BFS序,完整范围适配64MiB。

    易错点

    文章反复出现同一模式只给这一查询项贡献1,查询词重复则每个输入查询项分别计算。fail聚合不能正序进行。多组之间重建自动机,首行T用文本读入,之后按合法协议读到EOF处理各组,避免凭空给未限组数加int上界。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    struct Node {
        int first = -1, next = -1, fail = 0;
        char ch = 0;
        long long count = 0;
    };
    struct AC {
        vector<Node> v;
        vector<int> order;
        AC(size_t cap) {
            v.reserve(cap);
            v.emplace_back();
        }
        int edge(int u, char ch) {
            for (int x = v[u].first; x != -1; x = v[x].next)
                if (v[x].ch == ch)
                    return x;
            return -1;
        }
        int insert(const string &s, bool mark) {
            int u = 0;
            for (char ch : s) {
                int x = edge(u, ch);
                if (x < 0) {
                    x = v.size();
                    Node node;
                    node.ch = ch;
                    node.next = v[u].first;
                    v.push_back(node);
                    v[u].first = x;
                }
                u = x;
                if (mark)
                    v[u].count++;
            }
            return u;
        }
        void build() {
            order.reserve(v.size());
            for (int x = v[0].first; x != -1; x = v[x].next)
                order.push_back(x);
            for (size_t i = 0; i < order.size(); ++i) {
                int u = order[i];
                for (int x = v[u].first; x != -1; x = v[x].next) {
                    int f = v[u].fail;
                    while (f && edge(f, v[x].ch) < 0)
                        f = v[f].fail;
                    int t = edge(f, v[x].ch);
                    v[x].fail = t < 0 ? 0 : t;
                    order.push_back(x);
                }
            }
        }
        int step(int u, char ch) {
            while (u && edge(u, ch) < 0)
                u = v[u].fail;
            int x = edge(u, ch);
            return x < 0 ? 0 : x;
        }
        void aggregate() {
            for (size_t i = order.size(); i > 0; --i) {
                int u = order[i - 1];
                v[v[u].fail].count += v[u].count;
            }
        }
    };
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        string T;
        cin >> T;
        int n;
        while (cin >> n) {
            vector<string> w(n);
            size_t total = 1;
            for (auto &s : w) {
                cin >> s;
                total += s.size();
            }
            AC ac(total);
            vector<int> end;
            for (auto &s : w)
                end.push_back(ac.insert(s, false));
            ac.build();
            string text;
            cin >> text;
            int u = 0;
            for (char ch : text) {
                u = ac.step(u, ch);
                ac.v[u].count++;
            }
            ac.aggregate();
            int ans = 0;
            for (int x : end)
                ans += ac.v[x].count > 0;
            cout << ans << '\n';
        }
    }
    
    • 1

    信息

    ID
    1738
    时间
    1000ms
    内存
    64MiB
    难度
    普及
    标签
    递交数
    1
    已通过
    1
    上传者