普林斯顿算法课作业 part II 的python实现(四)Boggle

普林斯顿算法课作业 part II 的python实现(四)BoggleBoggle问题思路代码结果总结问题原问题网页:Boggle问题大致描述如下:有一个4×44\times44×4的方格图,每一个格子上标有一个英文字母,现要从中寻找出英文单词

问题

原问题网页:Boggle
问题大致描述如下:
有一个 4 × 4 4\times4 4×4 的方格图,每一个格子上标有一个英文字母,现要从中寻找出英文单词。
Boggle

规则如下:

  • 该单词由一系列 相邻 的格子组成。这里的相邻指上下左右和对角
  • 该单词长度至少为3
  • 每一个格子只能用一次
  • 该单词必须在一个预先给定的字典集内
    rule

然后按每一个单词的长度进行计分
计分

该问题要在给定一个单词集合一个字母网格的前提下,给出所有符合条件的单词,以及计算它们的分数之和。

思路

首先要存储字典集和字母网格的数据。这里采用字典树来存储字典集,字典树可以快速地查找某个单词是否在该字典集中。用一个Boggleboard类来存储字母网格,在Boggleboard类中用一个二维数组来存储网格上的字母,定义neighbours方法,来计算 ( i , j ) (i,j) (i,j) 位置处的相邻网格,以便接下来搜索使用。

利用深度优先搜索(Depth-First-Search)来搜索所有可能的单词。先定义节点node类,用来存储访问过的网格和当前所形成的字符串。每次从一个网格点出发,以该网格点为当前节点,进行深度优先搜索。如果该节点的当前字符串长度大于2且在预先给定的字典集中,那么就将其加入到有效单词集中,然后对于该网格的每一个neighbour,如果没有访问过这个neighbour,那就以这个neighbour为当前节点,更新访问集和当前字符串,然后递归进行搜索。

这样运行起来时间较长,可以在每次深度优先搜索的时候,进行一个判断:如果节点的当前字符串不是单词集的前缀,即单词集中的单词没有以该字符串开头的,那么就不用再进行搜索了,因为以该字符串开头的单词,肯定不在单词集里面,这样可以提高搜索的效率。

代码

# -*- coding: utf-8 -*-
""" Created on Sun Jan 10 15:04:57 2021 @author: zxw """

# 设置文件路径
import os
os.chdir('C:/Users/zxw/Desktop/修身/与自己/数据科学/算法/boggle')

# 参考https://blog.csdn.net/weixin_43204128/article/details/90633128
# 字典树
class Trie:
	def __init__(self):
		self.root = { 
   }  # 这里用一个字典存储
		self.end_of_word = '#'   # 用#标志一个单词的结束
	
	def insert(self, word: str):
		node = self.root
		for char in word:
			node = node.setdefault(char, { 
   })
		node[self.end_of_word] = self.end_of_word
	
	# 查找一个单词是否完整的在字典树里,所以最后一句判断node是否等于#
	def search(self, word: str):
		node = self.root
		for char in word:
			if char not in node:
				return False
			node = node[char]
		return self.end_of_word in node
		
	# 查找是否有一个单词是这个前缀开始的
	def startsWith(self, prefix: str):
		node = self.root
		for char in prefix:
			if char not in node:
				return False
			node = node[char]
		return True

# 建立Board类
class BoggleBoard:
    def __init__(self,boardname):
        with open(boardname,'r') as f:
            t = f.readlines()
        self.rows = int(t[0].strip().split()[0])
        self.cols = int(t[0].strip().split()[1])  
        self.Boggle = [[j for j in t[i+1].strip().split()] for i in range(self.rows)]
    def getLetter(self,i,j):
        return self.Boggle[i][j]
    # 每一个格子的邻居
    def neighbours(self,i,j):
        r = self.rows
        c = self.cols
        nghb = []
        if i != 0:
            nghb.append([i-1,j])
        if i != r-1:
            nghb.append([i+1,j])
        if j != 0:
            nghb.append([i,j-1])
        if j != c-1:
            nghb.append([i,j+1])
        if (i != 0) & (j != 0):
            nghb.append([i-1,j-1])
        if (i != 0) & (j != c-1):
            nghb.append([i-1,j+1])
        if (i != r-1) & (j != 0):
            nghb.append([i+1,j-1])        
        if (i != r-1) & (j != c-1):
            nghb.append([i+1,j+1])                
        return nghb

# 定义深度优先搜索的节点
class node:
    def __init__(self,board,i,j):
        self.visited = set() # 已访问过的点
        self.index = (i,j)
        self.nghb = [k for k in board.neighbours(i,j)]
        if board.Boggle[i][j] == 'Qu': #Qu情况的特殊处理
            self.char = 'QU'
        else:
            self.char = board.Boggle[i][j]
    

class BoggleSolver:
    def __init__(self, dictionary):
        self.dictionary = dictionary
        self.ValidWords = set()
        
    # 深度优先搜索
    def dfs(self,Node, board): 
        Node.visited.add(Node.index)
        # 若没有以当前字符串开头的单词,则跳过搜索
        if not self.dictionary.startsWith(Node.char): 
            pass
        else:
            # 如果字符串长度大于2且在字典中,则加入有效集
            if len(Node.char) > 2:
                if self.dictionary.search(Node.char):
                    self.ValidWords.add(Node.char)
            # 递归搜索其没有到达过的邻居
            for k in Node.nghb:
                node_k = node(board,k[0],k[1])
                if node_k.index not in Node.visited:
                    node_k.visited = Node.visited
                    node_k.char = Node.char + node_k.char
                    self.dfs(node_k,board)
        Node.visited.remove(Node.index) # !!!
        
    # 获得所有有效单词
    def getAllValidWords(self, board):
        self.ValidWords = set() 
        r = board.rows
        c = board.cols
        for i in range(r):
            for j in range(c):
                self.dfs(node(board, i, j),board)       
        return self.ValidWords
    
    # 计算每个单词所对应的分数 
    def scoreOf(self, word):
        if self.dictionary.search(word):
            if (len(word) == 3)|(len(word) == 4):
                return 1
            elif len(word) == 5:
                return 2
            elif len(word) == 6:
                return 3            
            elif len(word) == 7:
                return 5
            elif len(word) > 7:
                return 11            
        return 0
    
    # 获得所有单词分数之和
    def Allscores(self):
        score = 0
        for word in self.ValidWords:
            score = score + self.scoreOf(word)
        return score

# 测试
if __name__ == '__main__':
    dictionary = Trie()
    dict_name = 'dictionary-yawl.txt' #设置字典集
    with open(dict_name,'r') as f:
        t = f.readlines()
    for i in t:
        dictionary.insert(i.strip())
        
    boardname = 'board-points26539.txt' # 设置Board
    test_board = BoggleBoard(boardname)
    test_solver = BoggleSolver(dictionary)
    test_solver.getAllValidWords(test_board)       
    test_solver.Allscores()

                
                
                

结果

4*4
4*4

含Qu的情况
含Qu的情况6*66*6

总结

这次作业主要涉及以下内容:

  • 利用字典树存储字典集
  • 利用深度优先搜索递归搜索所有可能情况

今天的文章普林斯顿算法课作业 part II 的python实现(四)Boggle分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/62439.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注