# Trie# DFS# String# Greedy

f462 - Shortst Names

🔗 前往 ZeroJudge 原題

題目描述

題目要求計算在一個村莊中,如果每個人都使用最短的前綴來稱呼其他人(包括自己),總共需要多少個字元。村莊中每個人的名字都不會是其他人的名字的前綴。

解題思路

本題的核心思想是利用 Trie 樹來儲存所有的人名。Trie 樹的每個節點代表一個字元,從根節點到葉子節點的路徑代表一個完整的人名。

在建立 Trie 樹後,我們需要找到每個名字的最短唯一前綴。這意味著找到一個前綴,使得 Trie 樹中只有一個名字以該前綴開頭。我們可以通過深度優先搜尋 (DFS) 來遍歷 Trie 樹,並檢查每個節點的子樹大小。如果一個節點的子樹大小為 1,則該節點對應的前綴就是該名字的最短唯一前綴。

最後,將所有名字的最短唯一前綴的長度加總,即可得到所需的總字元數。

複雜度分析

  • 時間複雜度: O(N * M),其中 N 是人名數量,M 是人名的平均長度。建立 Trie 樹需要 O(N * M) 的時間,DFS 遍歷 Trie 樹也需要 O(N * M) 的時間。
  • 空間複雜度: O(N * M),Trie 樹的空間複雜度取決於人名的總長度。在最壞的情況下,所有的人名都不同,則需要儲存所有字元。

程式碼

#include <iostream>
using namespace std;
struct trie{
	trie* nxt[26];
	int cnt = 0;
	int sz = 0;
};
trie* root = new trie();
int t,n,ans;
string s;
void insert(string s){
	trie* now=root;
	for(int i=0;i<s.length();++i){
		now->sz++;
		if(now->nxt[s[i]-'a']==nullptr){
			now->nxt[s[i]-'a'] = new trie();
		}
		now = now->nxt[s[i]-'a'];
	}
	now->sz++;
	now->cnt++;
}
void dfs(trie* now,int it){
	if(now->sz==1&&it!=0){
		ans+=it;
		return;
	}
	for(int i=0;i<26;++i){
		if(now->nxt[i]!=nullptr){
			dfs(now->nxt[i],it+1);
		}
	}
	return;
}
int main(){
	cin.tie(0); ios::sync_with_stdio(false);
	cin >> t;
	while(t--){
		cin >> n;
		root = new trie();
		for(int i=0;i<n;++i){
			cin >> s;
			insert(s);
		}
		ans=0;
		dfs(root,0);
		cout << ans << "\n";
	}
}

Discussion