T9辞書はどのように機能しますか?その背後にあるデータ構造は何ですか。 「4663」と入力すると、ボタンを押したときに「good」が表示され、「gone」、「home」などになります。
編集:ユーザーが46と入力すると、「go」と表示され、下向き矢印を押すと「gone」などと表示されます。
4663
に翻訳する
{G、H、I} {M、N、O} {M、N、O} {D、E、F}
T9は、最初の可能な文字から順に、可能性を順にフィルタリングして機能します。したがって、例の最初のステップは、辞書リストをG、H、またはIで始まるすべての単語にフィルタリングすることです。次のステップでは、そのリストを使用して、2番目の文字をM、N、Oでフィルタリングします。
以前のように、T9はトライを使用しており、リンクはビットマップ(文字ごとに1ビット)で表されています。スティーブハノフが非常にうまく説明しているので、これは簡潔なデータ構造と呼ばれます:
T9はビットマップベースのTrieを使用していると思います。最初のレベルでは、各ビットが文字を表す32ビット(32ビットに拡張されていると思います)のWordがあります。 Wordの開始文字として有効なすべての文字には、ビットが設定されています。
次のレベルでは、前のレベルで設定された各ビットに対して1つの32ビットマップがあります。これらのマップでは、対応する文字がWordの2番目の文字として有効である場合、各ビットが設定され、最初の文字は最初のレベルによって決定されます。
その後、構造が続きます。文字ごとに1ビットを使用すると、非常に効率的なストレージになります。ただし、アドレッシングスキームは難しいものでなければなりません。短い単語には上記のスキーマを使用し、長い単語には別のスキーマを使用して、何らかの最適化を行うこともできます。
Trieを使用したC#の実装
public interface ICellT9
{
void Add(string a_name);
List<string> GetNames(string a_number);
}
public class Cell : ICellT9
{
private Dictionary<int, Cell> m_nodeHolder;
private List<string> m_nameList;
public Cell()
{
m_nameList = new List<string>();
m_nodeHolder = new Dictionary<int, Cell>();
for (int i = 2; i < 10; i++)
{
m_nodeHolder.Add(i, null);
}
}
public void Add(string a_name)
{
Add(a_name, a_name);
}
private void Add(string a_name, string a_originalName)
{
if (string.IsNullOrEmpty(a_name) &&
(string.IsNullOrEmpty(a_originalName) == false))
{
m_nameList.Add(a_originalName);
}
else
{
int l_firstNumber = CharToNumber(a_name[0].ToString());
if (m_nodeHolder[l_firstNumber] == null)
{
m_nodeHolder[l_firstNumber] = new Cell();
}
m_nodeHolder[l_firstNumber].Add(a_name.Remove(0, 1), a_originalName);
}
}
public List<string> GetNames(string a_number)
{
List<string> l_result = null;
if (string.IsNullOrEmpty(a_number))
{
return l_result;
}
int l_firstNumber = a_number[0] - '0';
if (a_number.Length == 1)
{
l_result = m_nodeHolder[l_firstNumber].m_nameList;
}
else if(m_nodeHolder[l_firstNumber] != null)
{
l_result = m_nodeHolder[l_firstNumber].GetNames(a_number.Remove(0, 1));
}
return l_result;
}
private int CharToNumber(string c)
{
int l_result = 0;
if (c == "a" || c == "b" || c == "c")
{
l_result = 2;
}
else if (c == "d" || c == "e" || c == "f")
{
l_result = 3;
}
else if (c == "g" || c == "h" || c == "i")
{
l_result = 4;
}
else if (c == "j" || c == "k" || c == "l")
{
l_result = 5;
}
else if (c == "m" || c == "n" || c == "o")
{
l_result = 6;
}
else if (c == "p" || c == "q" || c == "r" || c == "s")
{
l_result = 7;
}
else if (c == "t" || c == "u" || c == "v")
{
l_result = 8;
}
else if (c == "w" || c == "x" || c == "y" || c == "z")
{
l_result = 9;
}
return l_result;
}
}
私はおそらくそれを辞書から始めて、それから(各Wordについて)Wordを形成する可能性のある数字でキー付けされたリストにWordをプッシュします。
次に、おそらく結果のリストを何らかの方法で並べ替える必要があります。そうすれば、可能性の高い単語が可能性の低い単語の前に表示されます(スペースがある場合は、カウンター用の小さな領域も含めるので、増分的に再これらを並べ替えるOR最後に使用したものを候補リストの前に移動するだけなので、時間の経過とともに、ユーザーにより良い答えを与える傾向があります)。
このようにして、4663を入力として使用すると、関連するリンクリストを大まかにO(1)ハッシュテーブルルックアップ)で簡単に取得できます。