:heavy_check_mark: test/Tree/aho_corasick/yosupo-aho_corasick.cpp

Depends on

Code

// competitive-verifier: PROBLEM https://judge.yosupo.jp/problem/aho_corasick

#include "Tree/aho_corasick.hpp"
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int N;
    cin >> N;

    AhoCorasick<char> aho_cora;
    vector<string> S(N);
    vector<int> terminal_node_ids(N);
    for (int i = 0; i < N; i++)
    {
        cin >> S[i];
        terminal_node_ids[i] = aho_cora.insert(S[i]);
    }

    aho_cora.build();

    cout << aho_cora.size() << endl;
    for (int i = 1; i < aho_cora.size(); i++)
    {
        cout << aho_cora.parent(i) << " " << aho_cora.get_suffix_link(i) << endl;
    }

    for (int i = 0; i < N; i++)
    {
        cout << terminal_node_ids[i] << " ";
    }
    cout << endl;

    return 0;
}
#line 1 "test/Tree/aho_corasick/yosupo-aho_corasick.cpp"
// competitive-verifier: PROBLEM https://judge.yosupo.jp/problem/aho_corasick

#line 1 "Tree/aho_corasick.hpp"
#include <bits/stdc++.h>
using namespace std;

#line 2 "Tree/trie.hpp"
using namespace std;

template <class T>
struct Trie
{
    struct Node
    {
        // 子ノードへの遷移
        unordered_map<T, int> next_node_ids;

        // 親ノードのID
        // root の parent は -1
        int parent = -1;

        // parent からこのノードへ遷移するときに使った値
        // root では意味を持たない
        T transition_value{};

        // このノードを通過する文字列の個数
        int pass_count = 0;

        // このノードで終端する文字列の個数
        int end_count = 0;

        Node() {};

        Node(int parent, const T &transition_value)
            : parent(parent), transition_value(transition_value) {};
    };

    vector<Node> nodes;

    Trie() : nodes()
    {
        nodes.push_back(Node());
    };

    template <typename Container>
        requires same_as<typename Container::value_type, T>
    int insert(const Container &v)
    {
        int node_id = 0;
        nodes[node_id].pass_count++;

        for (const auto &c : v)
        {
            if (!nodes[node_id].next_node_ids.contains(c))
            {
                int next_node_id = nodes.size();
                nodes[node_id].next_node_ids[c] = next_node_id;

                // 新しく作るノードは、現在のノードを親として持つ
                nodes.push_back(Node(node_id, c));
            }

            node_id = nodes[node_id].next_node_ids[c];
            nodes[node_id].pass_count++;
        }

        nodes[node_id].end_count++;

        // 挿入した列に対応する終端ノードIDを返す
        return node_id;
    }

    template <typename Container, typename Func>
        requires same_as<typename Container::value_type, T> && invocable<Func, int, bool>
    void iterate_prefix(const Container &v, Func f)
    {
        int node_id = 0;

        for (auto itr = v.begin(); itr != v.end(); itr++)
        {
            if (!nodes[node_id].next_node_ids.contains(*itr))
            {
                return;
            }

            node_id = nodes[node_id].next_node_ids[*itr];

            // 第2引数は、現在のノードが入力列 v の終端かどうか
            f(node_id, next(itr) == v.end());
        }
    }

    template <typename Container>
        requires same_as<typename Container::value_type, T>
    void erase_subtrie(const Container &prefix)
    {
        int node_id = 0;
        vector<int> ancestors;

        for (const auto &c : prefix)
        {
            ancestors.push_back(node_id);

            if (!nodes[node_id].next_node_ids.contains(c))
            {
                return;
            }

            node_id = nodes[node_id].next_node_ids[c];
        }

        int removed_pass_count = nodes[node_id].pass_count;

        // prefix 以下の部分木を論理的に削除する
        nodes[node_id].pass_count = 0;
        nodes[node_id].end_count = 0;
        nodes[node_id].next_node_ids.clear();

        // 祖先の通過回数から、削除した部分木の通過回数を引く
        for (const auto &ancestor : ancestors)
        {
            nodes[ancestor].pass_count -= removed_pass_count;
        }
    }
};
#line 5 "Tree/aho_corasick.hpp"

template <class T>
struct AhoCorasick
{
    Trie<T> trie;

    // suffix_link[v]:
    // str(v) の真の接尾辞のうち、Trie に存在する最長のものに対応するノードID
    vector<int> suffix_link;

    // matched[v]:
    // v に到達した時点で、何らかの登録済みパターンが末尾にマッチしているか
    vector<bool> matched;

    // build 後に true
    bool built = false;

    AhoCorasick() = default;

    template <typename Container>
        requires same_as<typename Container::value_type, T>
    int insert(const Container &pattern)
    {
        built = false;
        return trie.insert(pattern);
    }

    void build()
    {
        const int node_count = trie.nodes.size();

        suffix_link.assign(node_count, 0);
        matched.assign(node_count, false);

        for (int node_id = 0; node_id < node_count; node_id++)
        {
            matched[node_id] = trie.nodes[node_id].end_count > 0;
        }

        queue<int> que;

        // root の子の suffix link は root
        for (const auto &[value, child] : trie.nodes[0].next_node_ids)
        {
            suffix_link[child] = 0;
            que.push(child);
        }

        while (!que.empty())
        {
            int node_id = que.front();
            que.pop();

            // suffix link 先でマッチしているなら、このノードもマッチ状態
            matched[node_id] = matched[node_id] || matched[suffix_link[node_id]];

            for (const auto &[value, child] : trie.nodes[node_id].next_node_ids)
            {
                suffix_link[child] = find_next(suffix_link[node_id], value);
                que.push(child);
            }
        }

        built = true;
    }

    int move(int node_id, const T &value) const
    {
        assert(built);

        return find_next(node_id, value);
    }

    bool is_matched(int node_id) const
    {
        assert(built);

        return matched[node_id];
    }

    int get_suffix_link(int node_id) const
    {
        assert(built);

        return suffix_link[node_id];
    }

    int parent(int node_id) const
    {
        return trie.nodes[node_id].parent;
    }

    T transition_value(int node_id) const
    {
        return trie.nodes[node_id].transition_value;
    }

    int size() const
    {
        return trie.nodes.size();
    }

private:
    int find_next(int node_id, const T &value) const
    {
        while (node_id != 0 && !trie.nodes[node_id].next_node_ids.contains(value))
        {
            node_id = suffix_link[node_id];
        }

        if (trie.nodes[node_id].next_node_ids.contains(value))
        {
            return trie.nodes[node_id].next_node_ids.at(value);
        }

        return 0;
    }
};
#line 5 "test/Tree/aho_corasick/yosupo-aho_corasick.cpp"
using namespace std;

int main()
{
    int N;
    cin >> N;

    AhoCorasick<char> aho_cora;
    vector<string> S(N);
    vector<int> terminal_node_ids(N);
    for (int i = 0; i < N; i++)
    {
        cin >> S[i];
        terminal_node_ids[i] = aho_cora.insert(S[i]);
    }

    aho_cora.build();

    cout << aho_cora.size() << endl;
    for (int i = 1; i < aho_cora.size(); i++)
    {
        cout << aho_cora.parent(i) << " " << aho_cora.get_suffix_link(i) << endl;
    }

    for (int i = 0; i < N; i++)
    {
        cout << terminal_node_ids[i] << " ";
    }
    cout << endl;

    return 0;
}

Test cases

Env Name Status Elapsed Memory
g++ almost_single_00 :heavy_check_mark: AC 1402 ms 220 MB
g++ almost_single_01 :heavy_check_mark: AC 1485 ms 219 MB
g++ almost_single_02 :heavy_check_mark: AC 1467 ms 219 MB
g++ almost_single_03 :heavy_check_mark: AC 1173 ms 171 MB
g++ almost_single_04 :heavy_check_mark: AC 1417 ms 220 MB
g++ almost_single_05 :heavy_check_mark: AC 1442 ms 220 MB
g++ almost_single_06 :heavy_check_mark: AC 1347 ms 209 MB
g++ almost_single_07 :heavy_check_mark: AC 1039 ms 155 MB
g++ bfs_00 :heavy_check_mark: AC 36 ms 5 MB
g++ bfs_01 :heavy_check_mark: AC 163 ms 22 MB
g++ bfs_02 :heavy_check_mark: AC 294 ms 37 MB
g++ bfs_03 :heavy_check_mark: AC 481 ms 43 MB
g++ example_00 :heavy_check_mark: AC 3 ms 3 MB
g++ example_01 :heavy_check_mark: AC 2 ms 3 MB
g++ example_02 :heavy_check_mark: AC 2 ms 3 MB
g++ example_03 :heavy_check_mark: AC 2 ms 3 MB
g++ fibonacci_00 :heavy_check_mark: AC 1415 ms 220 MB
g++ fibonacci_01 :heavy_check_mark: AC 1457 ms 220 MB
g++ fibonacci_02 :heavy_check_mark: AC 1429 ms 219 MB
g++ fibonacci_03 :heavy_check_mark: AC 1487 ms 217 MB
g++ fibonacci_04 :heavy_check_mark: AC 1416 ms 220 MB
g++ fibonacci_05 :heavy_check_mark: AC 1431 ms 220 MB
g++ fibonacci_06 :heavy_check_mark: AC 1488 ms 219 MB
g++ fibonacci_07 :heavy_check_mark: AC 1503 ms 216 MB
g++ fibonacci_08 :heavy_check_mark: AC 1434 ms 220 MB
g++ fibonacci_09 :heavy_check_mark: AC 1427 ms 220 MB
g++ fibonacci_10 :heavy_check_mark: AC 1446 ms 219 MB
g++ fibonacci_11 :heavy_check_mark: AC 1458 ms 215 MB
g++ fibonacci_12 :heavy_check_mark: AC 1410 ms 220 MB
g++ fibonacci_13 :heavy_check_mark: AC 1462 ms 220 MB
g++ fibonacci_14 :heavy_check_mark: AC 1439 ms 220 MB
g++ fibonacci_15 :heavy_check_mark: AC 1503 ms 216 MB
g++ random_00 :heavy_check_mark: AC 146 ms 38 MB
g++ random_01 :heavy_check_mark: AC 119 ms 27 MB
g++ random_02 :heavy_check_mark: AC 959 ms 107 MB
g++ random_03 :heavy_check_mark: AC 1289 ms 169 MB
g++ random_04 :heavy_check_mark: AC 1418 ms 220 MB
g++ random_05 :heavy_check_mark: AC 1444 ms 220 MB
g++ random_06 :heavy_check_mark: AC 1502 ms 220 MB
g++ random_07 :heavy_check_mark: AC 1518 ms 221 MB
g++ random_08 :heavy_check_mark: AC 146 ms 38 MB
g++ random_09 :heavy_check_mark: AC 106 ms 27 MB
g++ random_10 :heavy_check_mark: AC 377 ms 43 MB
g++ random_11 :heavy_check_mark: AC 1521 ms 210 MB
g++ random_12 :heavy_check_mark: AC 1443 ms 220 MB
g++ random_13 :heavy_check_mark: AC 1447 ms 220 MB
g++ random_14 :heavy_check_mark: AC 1440 ms 220 MB
g++ random_15 :heavy_check_mark: AC 1543 ms 220 MB
g++ random_16 :heavy_check_mark: AC 139 ms 38 MB
g++ random_17 :heavy_check_mark: AC 113 ms 27 MB
g++ random_18 :heavy_check_mark: AC 65 ms 10 MB
g++ random_19 :heavy_check_mark: AC 1463 ms 198 MB
g++ random_20 :heavy_check_mark: AC 1416 ms 220 MB
g++ random_21 :heavy_check_mark: AC 1506 ms 220 MB
g++ random_22 :heavy_check_mark: AC 1452 ms 219 MB
g++ random_23 :heavy_check_mark: AC 1504 ms 220 MB
g++ short_period_00 :heavy_check_mark: AC 1455 ms 220 MB
g++ short_period_01 :heavy_check_mark: AC 781 ms 153 MB
g++ short_period_02 :heavy_check_mark: AC 1068 ms 168 MB
g++ short_period_03 :heavy_check_mark: AC 171 ms 26 MB
g++ short_period_04 :heavy_check_mark: AC 1440 ms 220 MB
g++ short_period_05 :heavy_check_mark: AC 1418 ms 220 MB
g++ short_period_06 :heavy_check_mark: AC 545 ms 86 MB
g++ short_period_07 :heavy_check_mark: AC 184 ms 29 MB
g++ short_period_08 :heavy_check_mark: AC 1396 ms 220 MB
g++ short_period_09 :heavy_check_mark: AC 1410 ms 220 MB
g++ short_period_10 :heavy_check_mark: AC 746 ms 113 MB
g++ short_period_11 :heavy_check_mark: AC 397 ms 79 MB
g++ short_period_12 :heavy_check_mark: AC 1409 ms 220 MB
g++ short_period_13 :heavy_check_mark: AC 1410 ms 219 MB
g++ short_period_14 :heavy_check_mark: AC 1402 ms 218 MB
g++ short_period_15 :heavy_check_mark: AC 1214 ms 180 MB
Back to top page

This site uses Just the Docs, a documentation theme for Jekyll.