我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Python如何利用字典树实现猎词游戏

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Python如何利用字典树实现猎词游戏

本篇内容主要讲解“Python如何利用字典树实现猎词游戏”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python如何利用字典树实现猎词游戏”吧!

猎词(word hunt)是一类很常见的游戏,给你一张字母组成的表,然后让你在这些字母中尽可能多的去寻找单词。这类游戏有不同的变体,一类是你可以多次重复使用这些字母(这类游戏叫做猎词),或者你只能使用一次每个字母(这类游戏叫做字母重组)。你组出来的单词越长就得分越高,使用了所有字母就可以获得最高分。

这类游戏对计算机而言是很「容易」去完成的,而且要强调一个相当有用的数据结构叫做 “Trie”。

解决策略

让我们先拿出一个单词「MAINE」

首先要做的决定我们要如何处理这个问题。如果问题是字母重组,那么我们可以尝试所有可能的字母组合,然后看看它们是否是单词。这对字母重组是一个还不错的解决方案,但是对猎词而言就不能给我们多少帮助了,因为字母可以被重用。所以当你可能发现了单词 ”name” 时,你将再不会发现单词 “nine”。显然我们不能尝试穷尽这些字母所有可能的组合,因为我们不知道一个单词可能被重复多少次。因为这个原因,我们退步为搜索一个词典,去看这个词是否可以只由我们拥有的字母组成。当有一个很大的词典时,这可能耗费大量的时间,并且你每次换了一个词时都必须重复这一步。

作为替代,我们需要一个搜索词典的方法,可以快速告诉我们某个单词是否在词典中。这就是预测性文本结构 Trie 字典树的用武之地。

什么是 Trie?

Trie 是一个树数据结构 — 作为原本树节点储存一个与 key 相关联的值的代替 — 这个节点现在储存 key 本身。节点中的值可用于根据遍历次数来为某些叶子节点或概率值分配顺序。

Python如何利用字典树实现猎词游戏

维基百科中一个 Trie 的例子

上面这个 Trie 的例子由 “A”,“to”,“tea”,“ted”,“ten”,“i”,“in” 和 “inn” 生成。一旦一个像这样的 Trie 字典树结构被生成,去判断任何一个单词是否在这个 Trie 字典树中就是 O(n) 复杂度的。如果我在搜索 “ted”,我会消耗 O(1) 去寻找 “t”,然后从 “t” 节点再消耗 O(1) 去寻找 “e”,并且再从 “te” 节点消耗 O(1) 去到 “d”。

面对问题“这一堆字母在不在这个词典中?”,这就是一个「非常」快速的解答方案。我们首先要做的就是构建词典。

在 Python 中,这个步骤很简单。每个节点的样子都应该是一个词典。所以我们需要从一个空词典开始,然后对词典中的每一个单词,逐字母的检查下一个字母是否在我们的 Trie 字典树结构中,如果不在就添进去。现在,这听起来相当耗费时间,在某些方面也的确如此,但是它只需要完成一次。当 Trie 被建好后,你可以直接使用它而无需任何其它开销。

创建 Trie 字典树

我们需要从一个装满所有可能单词的列表开始(网上有很多这类资源),然后我们的词典加载函数可能长下面这样:

def load():    with open('words.txt') as wordFile:        wordList = wordFile.read().split()     trie = {}    for word in wordList:        addWordToTrie(trie, word)       return trie

我们需要一个函数来给 Trie 中添加单词。我们通过快速浏览 Trie 来检查每一个字母,判断我们是否需要添加一个新的 key。因为我们通过 key 来检索 python 中的字典,所以无需在每个节点储存一个 value。这是一个有自己的 key 值的新词典。

def addWordToTrie(trie, word, idx = 0):    if idx >= len(word):        return           if word[idx] not in d:        d[word[idx]] = {}    addWordToTrie(d[word[idx]], word, idx+1)

这里有一个简单的想法。我们接收的参数是当前所在位置的 Trie 字典树(注意在这个例子中,Trie 中的所有节点也是一个 Trie),这个单词,以及我们所查看的字母在单词中的索引。

如果索引超过了单词的长度,我们就停止!如果没有超过,我们需要检查是否这个字母已经在这个 Trie 中。如果这个字母不在这个 Trie 的下一层中,那么我们添加一个新的字典在这一层,当前这个字母就是字典的 key。然后,我们递归的调用这个函数,并且传入我们当前字母对应的词典(也就是 Trie),这个单词,以及下一个索引位置。

使用这两个函数,我们就构建了上面展示的 Trie 字典树。但是有一个问题摆在我们面前。我们如何知道我们找到的是一个「单词」,而不是一个真正的单词的前一「部分」呢?例如,在上面这个 Trie 的例子中,我们希望 “in” 可以像 “inn” 一样返回是一个单词,但是并不希望将 “te” 作为一个词典中的单词来返回。

为了完成这一点,当我们完成一个单词时,「必须」在这个节点中储存一个值。来回头重新审视一下我们的 addWordToTrie 函数,如果这个节点表示一个完整的单词,就将 “leaf” 这个 key 设置为 “True”。

def addWordToTrie(d, word, idx):    if idx >= len(word):        d['leaf']=True        return    if word[idx] not in d:        d[word[idx]] = {'leaf':False}    addWordToTrie(d[word[idx]], word, idx+1)

现在,无论何时我们完成一个单词,都要设置当前这个词典节点的 “leaf” 值为 True,或者我们添加一个新的节点,它的 “leaf” 值为 “False”。

当我们加载这个函数初始化时,应该是同样的设置 {‘leaf’:False},所以我们就无需再拿一个空的字符串来作为有效词的返回。

就是这样!我们已经创建了我们的 Trie 结构,接下来啥时候使用它了。

单词测试

找一个办法来进行尝试:从一个空的列表开始。对我们单词中的每个字母,检查我们的 Trie 字典树,看它是否在其中。如果在,就拿到这个词典子树再重新开始(这样我们可以检查重复的字母)。保持这样进行下去,直到我们找到一个 leaf 标志位为 true 的节点,或者我们在下一层的词典子树中找不到单词中的任何字母。如果我们发现了一个标记为 leaf 的节点,就把这个单词添到列表中。如果我们没有找到下一个词典子树,就返回并执行下一个字母。

def findWords(trie, word, currentWord):    myWords = [];    for letter in word:        if letter in trie:            newWord = currentWord + letter            if (trie[letter]['leaf']):                myWords.append(newWord)            myWords.extend(findWords(trie[letter], word, newWord))    return myWords

这里注意一下,我们正在构建一个新单词传递到列表中,但是我们也会递归的去寻找新的单词,用来扩展我们的列表。

有的读者可能已经发现了接下来的问题。如果字母重复怎么办呢?例如我们的单词是 “「TEEN」”,并且我们现在在 “TE” 节点上,我们已经在子树上检查了 “t“,这很好,然后我们在子树上检查 ”e“ 并发现 ”tee“ 是一个单词。我们将 ”tee“ 添加到列表中。但是单词的下一个字母又是 ”e“,所以我们再次找到了 ”tee“。有一些方法去解决这个问题,但是最简单的方法之一就是用集合代替列表。

def findWords(trie, word, currentWord):    myWords = set()          for letter in word:        if letter in trie:            newWord = currentWord + letter            if trie[letter]['leaf']:                myWords.add(newWord)            myWords = myWords.union(findWords(trie[letter], word, newWord))    return myWords

现在无论我们把同一个单词找到多少次,我们都可以保证列表中的唯一性。我们也可以将输入单词中的字母去重,进而节约处理时间。

就这样!利用这三个函数就可以通过我们输入的字母来找到所有可能在字典中的单词。来让我们把这些包到一个 main 函数里面,然后给一个输入,具体步骤我们已经完成了。

def main():    print('Loading dictionary...')    wordTrie = load()    print('Done\n')     word = raw_input("What letters should we use: ")    minLength = int(raw_input("What is the minimum word length: "))    print("")     count = 0;    for word in sorted(findWords(wordTrie, word, "")):        if len(word) >= minLength:            count = count+1            print(word)    print(str(count) + " words found.")

因为我们不是单词重组,所以我们找到了「太」多单词。使用上面提到的例子「MAINE」和一个我找到的词典 — 大约有 370000 个单词 — 这个程度发现了 208 个单词。这也是为什么我添加了一个最短单词长度的原因。限制单词长度至少为七,我们可以得到如下结果:

Loading dictionary…
 
Done
 
What letters should we use: maine
 
What is the minimum word length: 7
 
amninia
 
anaemia
 
anamnia
 
animine
 
emmenia
 
enamine
 
manienie
 
mannaia
 
meminna
 
miminae
 
minaean
 
11 words found.

加载词典消耗了大约半秒,后面的查找单词基本上感受不到明显的时间消耗。

为了一个单词去每次都重新建树是很低效的,所以最好可以重用它,要么是保存整个数据结构,要么尝试一次循环的查找多个单词。

到此,相信大家对“Python如何利用字典树实现猎词游戏”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Python如何利用字典树实现猎词游戏

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Python如何利用字典树实现猎词游戏

本篇内容主要讲解“Python如何利用字典树实现猎词游戏”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python如何利用字典树实现猎词游戏”吧!猎词(word hunt)是一类很常见的游戏,给
2023-07-02

怎么用Python+Tkinter实现经典井字棋小游戏

这篇文章主要讲解了“怎么用Python+Tkinter实现经典井字棋小游戏”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用Python+Tkinter实现经典井字棋小游戏”吧!演示介绍首
2023-06-29

如何利用Python实现星空大战游戏

小编给大家分享一下如何利用Python实现星空大战游戏,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一.游戏画面二.游戏结束画面三.游戏素材四.游戏代码星空飞碟大
2023-06-29

如何使用Java实现经典游戏2048

这篇文章主要介绍如何使用Java实现经典游戏2048,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!主要设计1、游戏面板生成显示2、方块设计3、键盘监听,方向键控制数字移动4、数字移动逻辑算法处理5、数字累加到2048
2023-06-29

如何利用C语言实现猜数字小游戏

这篇文章主要讲解了“如何利用C语言实现猜数字小游戏”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何利用C语言实现猜数字小游戏”吧!实现猜数字的游戏:要用程序完成以下几步:1、电脑自动生成随
2023-06-20

如何用Pygame实现经典外星人游戏

本篇内容介绍了“如何用Pygame实现经典外星人游戏”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!安装环境下载python3,或如Anaco
2023-06-26

如何利用PHP实现游戏需求

对不起,我无法提供实现特定编程功能的具体代码示例。如果您有任何其他问题或需要关于PHP编程的一般指导,请随时告诉我。我会很乐意提供帮助。以上就是如何利用PHP实现游戏需求的详细内容,更多请关注编程网其它相关文章!
如何利用PHP实现游戏需求
2024-03-10

如何使用Java实现经典游戏推箱子

这篇文章将为大家详细讲解有关如何使用Java实现经典游戏推箱子,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。主要设计1、游戏面板生成显示2、地图生成算法3、人物移动算法4、播放背景音乐5、箱子移动算法6、
2023-06-29

如何利用js+canvas实现扫雷游戏

这篇文章主要介绍“如何利用js+canvas实现扫雷游戏”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“如何利用js+canvas实现扫雷游戏”文章能帮助大家解决问题。代码如下 胜
2023-07-02

如何利用pygame实现贪吃蛇游戏

这篇文章主要介绍如何利用pygame实现贪吃蛇游戏,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!创建蛇首先,先分析一下蛇的移动,不然我们一定会吃亏的(别问,问就是自己写了一堆无效代码)。蛇的移动其实并没有想象中那样复
2023-06-15

如何使用Python实现字典合并

这篇文章给大家分享的是有关如何使用Python实现字典合并的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1、用for循环把一个字典合并到另一个字典把a字典合并到b字典中,相当于用for循环遍历a字典,然后取出a字
2023-06-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录