ABC353E字典樹處理最長公共字首

加固文明幻景發表於2024-05-11

https://atcoder.jp/contests/abc353/tasks/abc353_e

其實就是字典樹板子題。

似乎遇到最長公共字首,就該想到字典樹。

依次加入每個字串:

維護一個陣列 siz 來統計在當前串之前的串在對應點的出現次數。

手模一下字典樹的建樹過程,顯然如果當前串 \(S_i\) 能跑到一個曾經串 \(S_j\) 也出現過的點,那麼這一個點肯定是 \(S_i\)\(S_j\) 最長公共字首的一部分,所以全部加入答案。

template <typename T>
struct Trie {
    int m;
    int N = 2e6;
    int now, tot;
    i64 ans;
    std::vector<T> val;
    std::vector<int> siz;//用來維護在加入這個字串之前有多少個字串到過字典樹的這個位置
    std::vector<std::map<T, int> > nxt;
    Trie():val(N), siz(1, 0), tot(1), nxt(1, std::map<T, int>()), ans(0){}

    void add(std::string s) {
        now = 0;
        for (int i = 0; i < s.size(); i++){
            if (nxt[now][s[i]] == 0) {
                nxt[now][s[i]] = tot; siz.push_back(0); val[tot++] = s[i];
            }
            now = nxt[now][s[i]];
            ans += siz[now];//只要當前這個字串也能到,那肯定是和之前字串的公共字首的一部分,加入進去
            siz[now] += 1;
            nxt.push_back(std::map<T, int>());
        }
    }
};

signed main() {

    std::cin.tie(nullptr)->sync_with_stdio(false);

    int n;
    std::cin >> n;
    Trie<char> trie;
    std::string s;
    for (int i = 0; i < n; i++) {
        std::cin >> s;
        trie.add(s);
    }

    std::cout << trie.ans << '\n';

    return 0;
}

相關文章