【问题标题】:Data structure behind T9 type of dictionaryT9类型字典背后的数据结构
【发布时间】:2010-04-04 09:26:58
【问题描述】:

T9 词典是如何工作的?它背后的数据结构是什么。如果我们输入“4663”,当我们按下按钮时,我们会得到“good”,然后是“gone”,然后是“home”等等......

编辑:如果用户输入 46,那么它应该显示 'go' 并且当按下向下箭头时应该显示 'gone' 等...

【问题讨论】:

    标签: algorithm data-structures mobile


    【解决方案1】:

    它可以通过多种方式实现,其中之一是Trie。路线由数字表示,节点指向单词集合。

    它也可以使用嵌套哈希表来实现,hash table 的键是一个字母,算法在每个数字上计算所有可能的路由(O(3^n) 路由)。

    【讨论】:

    • 您能否详细介绍一下嵌套哈希表的解决方案?我不明白为什么嵌套哈希表比整个单词上的简单哈希表更好(例如使用multimap),这仍然是 O(3^n) 的平均情况。
    • +1 for Trie,对我来说这是一个可以接受且优雅的解决方案。
    【解决方案2】:

    4663

    翻译成

    {G,H,I}{M,N,O}{M,N,O}{D,E,F}

    T9 的工作原理是从第一个可能的字母开始依次向下过滤可能性。因此,您示例中的第一步是将字典列表过滤为所有以 G、H 或 I 开头的单词。下一步,获取该列表并按 M、N、O 过滤第二个字母。依此类推......

    【讨论】:

    • 这让我清楚地了解了 T9 的工作原理,感谢您的简单解释!
    【解决方案3】:

    我想,就像之前的 T9 使用 trie 一样,链接由位图表示(每个字母 1 位)。这被称为简洁的数据结构,正如 Steve Hanov 很好地解释的那样:

    http://stevehanov.ca/blog/index.php?id=120

    【讨论】:

      【解决方案4】:

      我认为 T9 使用的是基于位图的 Trie。在第一层有一个 32 位(我假设它们扩展为 32)字,其中每个位代表一个字母。有效作为单词的起始字母的所有字母都设置了它们的位。

      在下一个级别中,对于在前一个级别中设置的每个位,都有一个 32 位映射。在这些映射中,如果对应的字母作为单词中的第二个字母有效,则设置每个位,起始字母由第一级决定。

      然后结构继续。使用每个字母一位可以实现非常有效的存储。然而,寻址方案必须具有挑战性。使用上述模式对短词进行某种优化,同时对长词使用其他方式。

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          我可能会从字典开始,然后(对于每个单词)将单词推送到一个列表中,该列表由可以构成该单词的数字作为键。

          然后,您可能需要以某种方式对结果列表进行排序,因此更可能出现的单词出现在不太可能出现的单词之前(如果您有空间,我还会为计数器添加一个小区域,这样我们就可以逐步重新排序这些或简单地将最后使用的以太网移到建议列表的前面,因此随着时间的推移,我们倾向于为用户提供更好的答案。

          这样,当你有 4663 作为输入时,你可以很简单地通过大约 O(1) 的哈希表查找来检索相关的链表。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-12-10
            • 2021-01-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多