最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python利用字典樹實(shí)現(xiàn)獵詞游戲

 更新時(shí)間:2022年06月15日 14:51:08   作者:Python中文社區(qū)  
獵詞(word hunt)是一類很常見的游戲,給你一張字母組成的表,然后讓你在這些字母中盡可能多的去尋找單詞。這類游戲用字典樹就能輕松完成,本文就來具體講講實(shí)現(xiàn)步驟,需要的可以參考一下

獵詞(word hunt)是一類很常見的游戲,給你一張字母組成的表,然后讓你在這些字母中盡可能多的去尋找單詞。這類游戲有不同的變體,一類是你可以多次重復(fù)使用這些字母(這類游戲叫做獵詞),或者你只能使用一次每個(gè)字母(這類游戲叫做字母重組)。你組出來的單詞越長就得分越高,使用了所有字母就可以獲得最高分。

這類游戲?qū)τ?jì)算機(jī)而言是很「容易」去完成的,而且要強(qiáng)調(diào)一個(gè)相當(dāng)有用的數(shù)據(jù)結(jié)構(gòu)叫做 “Trie”。

解決策略

讓我們先拿出一個(gè)單詞「MAINE」。

首先要做的決定我們要如何處理這個(gè)問題。如果問題是字母重組,那么我們可以嘗試所有可能的字母組合,然后看看它們是否是單詞。這對字母重組是一個(gè)還不錯(cuò)的解決方案,但是對獵詞而言就不能給我們多少幫助了,因?yàn)樽帜缚梢员恢赜谩K援?dāng)你可能發(fā)現(xiàn)了單詞 ”name” 時(shí),你將再不會發(fā)現(xiàn)單詞 “nine”。顯然我們不能嘗試窮盡這些字母所有可能的組合,因?yàn)槲覀儾恢酪粋€(gè)單詞可能被重復(fù)多少次。因?yàn)檫@個(gè)原因,我們退步為搜索一個(gè)詞典,去看這個(gè)詞是否可以只由我們擁有的字母組成。當(dāng)有一個(gè)很大的詞典時(shí),這可能耗費(fèi)大量的時(shí)間,并且你每次換了一個(gè)詞時(shí)都必須重復(fù)這一步。

作為替代,我們需要一個(gè)搜索詞典的方法,可以快速告訴我們某個(gè)單詞是否在詞典中。這就是預(yù)測性文本結(jié)構(gòu) Trie 字典樹的用武之地。

什么是 Trie?

Trie 是一個(gè)樹數(shù)據(jù)結(jié)構(gòu) — 作為原本樹節(jié)點(diǎn)儲存一個(gè)與 key 相關(guān)聯(lián)的值的代替 — 這個(gè)節(jié)點(diǎn)現(xiàn)在儲存 key 本身。節(jié)點(diǎn)中的值可用于根據(jù)遍歷次數(shù)來為某些葉子節(jié)點(diǎn)或概率值分配順序。

維基百科中一個(gè) Trie 的例子

上面這個(gè) Trie 的例子由 “A”,“to”,“tea”,“ted”,“ten”,“i”,“in” 和 “inn” 生成。一旦一個(gè)像這樣的 Trie 字典樹結(jié)構(gòu)被生成,去判斷任何一個(gè)單詞是否在這個(gè) Trie 字典樹中就是 O(n) 復(fù)雜度的。如果我在搜索 “ted”,我會消耗 O(1) 去尋找 “t”,然后從 “t” 節(jié)點(diǎn)再消耗 O(1) 去尋找 “e”,并且再從 “te” 節(jié)點(diǎn)消耗 O(1) 去到 “d”。

面對問題“這一堆字母在不在這個(gè)詞典中?”,這就是一個(gè)「非?!?/strong>快速的解答方案。我們首先要做的就是構(gòu)建詞典。

在 Python 中,這個(gè)步驟很簡單。每個(gè)節(jié)點(diǎn)的樣子都應(yīng)該是一個(gè)詞典。所以我們需要從一個(gè)空詞典開始,然后對詞典中的每一個(gè)單詞,逐字母的檢查下一個(gè)字母是否在我們的 Trie 字典樹結(jié)構(gòu)中,如果不在就添進(jìn)去?,F(xiàn)在,這聽起來相當(dāng)耗費(fèi)時(shí)間,在某些方面也的確如此,但是它只需要完成一次。當(dāng) Trie 被建好后,你可以直接使用它而無需任何其它開銷。

創(chuàng)建 Trie 字典樹

我們需要從一個(gè)裝滿所有可能單詞的列表開始(網(wǎng)上有很多這類資源),然后我們的詞典加載函數(shù)可能長下面這樣:

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

我們需要一個(gè)函數(shù)來給 Trie 中添加單詞。我們通過快速瀏覽 Trie 來檢查每一個(gè)字母,判斷我們是否需要添加一個(gè)新的 key。因?yàn)槲覀兺ㄟ^ key 來檢索 python 中的字典,所以無需在每個(gè)節(jié)點(diǎn)儲存一個(gè) value。這是一個(gè)有自己的 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)

這里有一個(gè)簡單的想法。我們接收的參數(shù)是當(dāng)前所在位置的 Trie 字典樹(注意在這個(gè)例子中,Trie 中的所有節(jié)點(diǎn)也是一個(gè) Trie),這個(gè)單詞,以及我們所查看的字母在單詞中的索引。

如果索引超過了單詞的長度,我們就停止!如果沒有超過,我們需要檢查是否這個(gè)字母已經(jīng)在這個(gè) Trie 中。如果這個(gè)字母不在這個(gè) Trie 的下一層中,那么我們添加一個(gè)新的字典在這一層,當(dāng)前這個(gè)字母就是字典的 key。然后,我們遞歸的調(diào)用這個(gè)函數(shù),并且傳入我們當(dāng)前字母對應(yīng)的詞典(也就是 Trie),這個(gè)單詞,以及下一個(gè)索引位置。

使用這兩個(gè)函數(shù),我們就構(gòu)建了上面展示的 Trie 字典樹。但是有一個(gè)問題擺在我們面前。我們?nèi)绾沃牢覀冋业降氖且粋€(gè)「單詞」,而不是一個(gè)真正的單詞的前一「部分」呢?例如,在上面這個(gè) Trie 的例子中,我們希望 “in” 可以像 “inn” 一樣返回是一個(gè)單詞,但是并不希望將 “te” 作為一個(gè)詞典中的單詞來返回。

為了完成這一點(diǎn),當(dāng)我們完成一個(gè)單詞時(shí),「必須」在這個(gè)節(jié)點(diǎn)中儲存一個(gè)值。來回頭重新審視一下我們的 addWordToTrie 函數(shù),如果這個(gè)節(jié)點(diǎn)表示一個(gè)完整的單詞,就將 “leaf” 這個(gè) key 設(shè)置為 “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)

現(xiàn)在,無論何時(shí)我們完成一個(gè)單詞,都要設(shè)置當(dāng)前這個(gè)詞典節(jié)點(diǎn)的 “leaf” 值為 True,或者我們添加一個(gè)新的節(jié)點(diǎn),它的 “leaf” 值為 “False”。

當(dāng)我們加載這個(gè)函數(shù)初始化時(shí),應(yīng)該是同樣的設(shè)置 {‘leaf’:False},所以我們就無需再拿一個(gè)空的字符串來作為有效詞的返回。

就是這樣!我們已經(jīng)創(chuàng)建了我們的 Trie 結(jié)構(gòu),接下來啥時(shí)候使用它了。

單詞測試

找一個(gè)辦法來進(jìn)行嘗試:從一個(gè)空的列表開始。對我們單詞中的每個(gè)字母,檢查我們的 Trie 字典樹,看它是否在其中。如果在,就拿到這個(gè)詞典子樹再重新開始(這樣我們可以檢查重復(fù)的字母)。保持這樣進(jìn)行下去,直到我們找到一個(gè) leaf 標(biāo)志位為 true 的節(jié)點(diǎn),或者我們在下一層的詞典子樹中找不到單詞中的任何字母。如果我們發(fā)現(xiàn)了一個(gè)標(biāo)記為 leaf 的節(jié)點(diǎn),就把這個(gè)單詞添到列表中。如果我們沒有找到下一個(gè)詞典子樹,就返回并執(zhí)行下一個(gè)字母。

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

這里注意一下,我們正在構(gòu)建一個(gè)新單詞傳遞到列表中,但是我們也會遞歸的去尋找新的單詞,用來擴(kuò)展我們的列表。

有的讀者可能已經(jīng)發(fā)現(xiàn)了接下來的問題。如果字母重復(fù)怎么辦呢?例如我們的單詞是 “「TEEN」”,并且我們現(xiàn)在在 “TE” 節(jié)點(diǎn)上,我們已經(jīng)在子樹上檢查了 “t“,這很好,然后我們在子樹上檢查 ”e“ 并發(fā)現(xiàn) ”tee“ 是一個(gè)單詞。我們將 ”tee“ 添加到列表中。但是單詞的下一個(gè)字母又是 ”e“,所以我們再次找到了 ”tee“。有一些方法去解決這個(gè)問題,但是最簡單的方法之一就是用集合代替列表。

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
 

現(xiàn)在無論我們把同一個(gè)單詞找到多少次,我們都可以保證列表中的唯一性。我們也可以將輸入單詞中的字母去重,進(jìn)而節(jié)約處理時(shí)間。

就這樣!利用這三個(gè)函數(shù)就可以通過我們輸入的字母來找到所有可能在字典中的單詞。來讓我們把這些包到一個(gè) main 函數(shù)里面,然后給一個(gè)輸入,具體步驟我們已經(jīng)完成了。

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.")

因?yàn)槲覀儾皇菃卧~重組,所以我們找到了「太」多單詞。使用上面提到的例子「MAINE」和一個(gè)我找到的詞典 — 大約有 370000 個(gè)單詞 — 這個(gè)程度發(fā)現(xiàn)了 208 個(gè)單詞。這也是為什么我添加了一個(gè)最短單詞長度的原因。限制單詞長度至少為七,我們可以得到如下結(jié)果:

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.

加載詞典消耗了大約半秒,后面的查找單詞基本上感受不到明顯的時(shí)間消耗。

為了一個(gè)單詞去每次都重新建樹是很低效的,所以最好可以重用它,要么是保存整個(gè)數(shù)據(jù)結(jié)構(gòu),要么嘗試一次循環(huán)的查找多個(gè)單詞。

總結(jié)

但愿這篇文章可以為你提供一個(gè) Trie 的基本介紹,便于你去解決一些單詞問題。當(dāng)你想要一些自動補(bǔ)充完成的任務(wù)時(shí),Trie 是一個(gè)很好用的數(shù)據(jù)結(jié)構(gòu)。短信,搜索甚至是指引方向,都可以使用系統(tǒng)中的數(shù)據(jù)構(gòu)建 Trie 來幫助預(yù)測用戶下一步想要輸入什么。正如我們所看到的,它也是在一個(gè)搜索大量的現(xiàn)有路徑時(shí)很好的結(jié)構(gòu),在這個(gè)例子中,這個(gè)路徑就是有效的單詞。

以上就是Python利用字典樹實(shí)現(xiàn)獵詞游戲的詳細(xì)內(nèi)容,更多關(guān)于Python字典樹的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • python讀取一個(gè)目錄下所有txt里面的內(nèi)容方法

    python讀取一個(gè)目錄下所有txt里面的內(nèi)容方法

    今天小編就為大家分享一篇python讀取一個(gè)目錄下所有txt里面的內(nèi)容方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-06-06
  • Flask框架搭建虛擬環(huán)境的步驟分析

    Flask框架搭建虛擬環(huán)境的步驟分析

    這篇文章主要介紹了Flask框架搭建虛擬環(huán)境的步驟,結(jié)合實(shí)例形式分析總結(jié)了flask框架搭建虛擬環(huán)境的步驟、常見問題與解決方法,需要的朋友可以參考下
    2019-12-12
  • python使用NumPy文件的讀寫操作

    python使用NumPy文件的讀寫操作

    這篇文章主要介紹了python使用NumPy讀寫文本文件。想了解第三方庫文件操作的同學(xué),來看一下吧
    2021-04-04
  • 使用Python的Treq on Twisted來進(jìn)行HTTP壓力測試

    使用Python的Treq on Twisted來進(jìn)行HTTP壓力測試

    這篇文章主要介紹了使用Python的Treq on Twisted來進(jìn)行HTTP壓力測試,基于Python中的Twisted框架,需要的朋友可以參考下
    2015-04-04
  • Python一行代碼快速實(shí)現(xiàn)程序進(jìn)度條示例

    Python一行代碼快速實(shí)現(xiàn)程序進(jìn)度條示例

    這篇文章主要為大家介紹了Python一行代碼快速實(shí)現(xiàn)程序進(jìn)度條示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • python小程序之4名牌手洗牌發(fā)牌問題解析

    python小程序之4名牌手洗牌發(fā)牌問題解析

    這篇文章主要為大家詳細(xì)介紹了python小程序之4名牌手洗牌發(fā)牌問題,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • Django關(guān)于事務(wù)transaction.atomic()的使用方式

    Django關(guān)于事務(wù)transaction.atomic()的使用方式

    這篇文章主要介紹了Django關(guān)于事務(wù)transaction.atomic()的使用方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • Python實(shí)現(xiàn)端口檢測的方法

    Python實(shí)現(xiàn)端口檢測的方法

    這篇文章主要介紹了Python實(shí)現(xiàn)端口檢測的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-07-07
  • python排序的方法實(shí)例總結(jié)

    python排序的方法實(shí)例總結(jié)

    在本篇文章里小編給大家整理的是一篇關(guān)于python排序的方法實(shí)例總結(jié)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。
    2021-04-04
  • 淺述python中深淺拷貝原理

    淺述python中深淺拷貝原理

    Python中,對象的賦值,拷貝(深/淺拷貝)之間是有差異的,如果使用的時(shí)候不注意,就可能產(chǎn)生意外的結(jié)果,其實(shí)這個(gè)是由于共享內(nèi)存導(dǎo)致的結(jié)果,下面我們來簡單談下Python中的深拷貝和淺拷貝。
    2018-09-09

最新評論

文化| 西和县| 德钦县| 双辽市| 苍南县| 新安县| 定陶县| 广南县| 蕉岭县| 临邑县| 宜兴市| 阿勒泰市| 布尔津县| 封开县| 石嘴山市| 南雄市| 左贡县| 信宜市| 青州市| 辉南县| 永昌县| 手游| 博湖县| 衡山县| 正定县| 崇州市| 突泉县| 南丹县| 晋城| 祁东县| 武定县| 抚远县| 浠水县| 方城县| 嘉义市| 洛宁县| 侯马市| 昌邑市| 凌云县| 普兰县| 奇台县|