# Trie# String# Greedy

f463 - Cellphone Typing

🔗 前往 ZeroJudge 原題

題目描述

題目描述了一種手機輸入法,它會根據字典自動補全單詞。目標是計算在給定字典下,輸入字典中所有單詞的平均擊鍵次數。如果一個前綴在字典中唯一確定了下一個字符,則該字符會自動輸入,無需用戶擊鍵。

解題思路

本題的核心思想是利用 Trie 樹來高效地查詢字典,並模擬手機輸入法的自動補全過程。

  1. 構建 Trie 樹: 將字典中的所有單詞插入到 Trie 樹中。在插入過程中,記錄每個節點的子節點數量(ct)以及是否為一個單詞的結尾(has)。
  2. 計算擊鍵次數: 對於字典中的每個單詞,從根節點開始遍歷,模擬輸入過程。如果當前節點的子節點數量為 1 且不是單詞結尾,則表示下一個字符可以自動補全,無需擊鍵。否則,需要擊鍵。
  3. 計算平均擊鍵次數: 統計所有單詞的總擊鍵次數,然後除以單詞的數量,得到平均擊鍵次數。

複雜度分析

  • 時間複雜度: O(N * M),其中 N 是字典中單詞的數量,M 是單詞的平均長度。構建 Trie 樹需要 O(N * M) 的時間,遍歷每個單詞計算擊鍵次數也需要 O(N * M) 的時間。
  • 空間複雜度: O(N * M),Trie 樹的空間複雜度取決於字典中所有單詞的總長度。在最壞情況下,所有單詞都具有不同的前綴,導致 Trie 樹的節點數量與總長度成正比。

程式碼

#include <bits/stdc++.h>
using namespace std;
int n;
string a[100005];
struct trie{
	int ct=0;
	bool has=0;
	trie* nxt[26];
};
int main(){
	cin.tie(0);cout.tie(0); ios::sync_with_stdio(0);
	while(cin >> n){
		int v=0;
		trie* root = new trie();
		for(int i=0;i<n;++i){
			cin >> a[i];
			v+=a[i].size();
			trie* it=root;
			for(int j=0;j<a[i].size();++j){
				if(it->nxt[a[i][j]-'a']==nullptr){
					++it->ct;
					it->nxt[a[i][j]-'a']=new trie();
				}
				it=it->nxt[a[i][j]-'a'];
				if(j==a[i].size()-1)it->has=1;
			}
		}
		for(int i=0;i<n;++i){
			trie* it=root;
			for(int j=0;j<a[i].size();++j){
				if(it->ct==1&&it->has==0&&j)--v;
				it=it->nxt[a[i][j]-'a'];
			}
		}
		double ans=round((double)v/(double)n*100)/100;
		cout << fixed << setprecision(2) << ans << "\n";
	}
}

Discussion