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;
}