leetcode刷题第二部分

这一次主要是学习了字符串的相关知识和算法。前面的基础的字符串的基础题目没什么好说的,都是非常基础的问题,用的知识也是比较简单的。

其实我有点想说一下这个Brute Force算法,虽然说效率有点低,但是架不住真的暴力好用,逻辑简单。有的时候如果并不要求算法有多精炼,其实可以参考这种简单粗暴的方法一用一个爽。

暴力匹配虽然简单,但是有的时候数字一大再加上如此低的效率,可能会磨掉任何一个人的耐心。而现在的RK算法确实是一个非常好的创新。

在这么多天的计算机学习中,我发现,其实有许多概念,之前都没有接触过。在RK算法之前,我一直在想这哈希表到底创建出来还有什么用呢?在这里,这个哈希表便是一种很好的匹配方式。我认为很好的用到了哈希表的特性。就像数学中,一个一个匹配的编码一样。

其后的kmp算法其实我们老师之前就跟我们讲过。但是讲过,让我再看一遍完全又是一种不同的感受。在这么多次的学习中,我感觉到这些之前的牛人都是先想到了这些更简单的方法来提升效率,然后再引进一些概念和数学模型来简化自己的语言,用这些模型来提升自己程序的效率。之前,我们老师就先让我们自己想这么简化暴力算法,我当时,能够想到这些重复的模式串的前缀可以稍微修改一下使得程序变得更高效,但是我还是没有想到kmp这么简单的算法。直接生成一个next数组直接移动,这样多简洁。关键是这个还是以模式串为基础进行生成的。这样的话这个算法的泛用性也提升上去了。(当时给我幼小的心灵极大的震撼!)

现在就说一下字典树。这个部分其实学了树还是要相对简单一点。(指理解)虽然说,知识点不是特别难,但是我学到这里的时候始终提醒我自己,学知识的时候要分的清楚轻重。而在这个部分,很明显,由于是字符串的匹配,那字典树的查找显然就是相对来说最重要的部分了。那其他的部分,了解一下,不要放太大的精力对我们的学习是非常有帮助的。

好的,废话不多说,进入解题环节吧。

这个题目其实和字典树有点类似之处,写了这个题目是很好的理解字典树的。

# 定义一个节点类,用于存储字母和子节点
class TrieNode:
    def __init__(self):
        self.children = {} 
        self.is_end = False 

# 定义一个前缀树类,用于插入,搜索和判断前缀
class Trie:
    def __init__(self):
        self.root = TrieNode() 

    def insert(self, word: str) -> None:
        # 向前缀树中插入字符串 word
        node = self.root 
        for char in word: 
            if char not in node.children:
                node.children[char] = TrieNode() 
            node = node.children[char] 
        node.is_end = True 
    def search(self, word: str) -> bool:
        # 如果字符串 word 在前缀树中,返回 True;否则,返回 False
        node = self.root 
        for char in word: 
            if char not in node.children: 
                return False 
            node = node.children[char] 
        return node.is_end 

    def startsWith(self, prefix: str) -> bool:
        # 如果之前已经插入的字符串 word 的前缀之一为 prefix,返回 True;否则,返回 False
        node = self.root 
        for char in prefix: 
            if char not in node.children: 
                return False 
            node = node.children[char] 
        return True 

第二题其实实际上是一个类似于rk算法中的计算哈希值的一个东西,也不算特别难。

# 定义一个 MapSum 类,用于存储键值对和计算前缀和
class MapSum:
    def __init__(self):
        self.map = {}
        self.prefix = {} 

    def insert(self, key: str, val: int) -> None:
        # 插入 key-val 键值对,如果键 key 已经存在,那么原来的键值对将被替代成新的键值对
        if key in self.map: 
            old_val = self.map[key] 
        else: 
            old_val = 0 
        self.map[key] = val 
        for i in range(len(key) + 1): 
            prefix = key[:i] 
            self.prefix[prefix] = self.prefix.get(prefix, 0) - old_val + val 

    def sum(self, prefix: str) -> int:
        # 返回所有以该前缀 prefix 开头的键 key 的值的总和
        return self.prefix.get(prefix, 0) 

第三题,驼峰匹配,那关键词肯定就是驼峰了,这个题目注意只要出现其他的大写字母统统是错误的,就像那个驼峰一样。除此之外,还有小写字母的匹配,其他的字母只要能够插入就不是问题了。

# 定义一个匹配模式串的函数,用于判断待查询列表 queries 中的每一项是否可以通过在模式串 pattern 中间插入小写字母得到
def match_pattern(queries: list, pattern: str) -> list:
    # 参数是一个字符串列表 queries 和一个字符串 pattern,返回值是一个布尔值列表
    ans = [] 
    for query in queries: 
        i = 0 
        match = True 
        for char in query: 
            if i < len(pattern) and char == pattern[i]: 
                i += 1 
            elif char.islower(): 
                pass 
            else: 
                match = False 
                break 
        if i != len(pattern): 
            match = False
        ans.append(match) 
    return ans 

第四题,虽然说是把重点放在字符串的匹配上,但是其他的知识也是需要了解的,不然很多东西都不知道,那这道题就无法解答了。

# 定义一个词典类 WordDictionary,用于存储和匹配单词
class WordDictionary:
    def __init__(self):
        self.root = {} 

    def addWord(self, word: str) -> None:
        # 将 word 添加到数据结构中,之后可以对它进行匹配
        node = self.root 
        for char in word: 
            if char not in node: 
                node[char] = {} 
            node = node[char] 
        node['$'] = True 

    def search(self, word: str) -> bool:
       
        return self.match(word, 0, self.root) 
    def match(self, word: str, index: int, node: dict) -> bool:
    
        if index == len(word): 
            return '$' in node 
        char = word[index] 
        if char == '.':
            for child in node.values(): 
                if self.match(word, index + 1, child): 
                    return True 
            return False
        else: 
            if char not in node: 
                return False 
            return self.match(word, index + 1, node[char]) 

第五题,先找到最短的那个词根,再使用疯狂108拳(暴力算法)直接非常简单就把这道题拿捏了(每个单词其实并不是很长,暴力算法又太简单了,架不住诱惑。)

# 定义一个将句子中有词根的单词用词根替换掉的函数,参数是一个由许多词根组成的字典列表 dictionary 和一个句子字符串 sentence,返回值是一个替换之后的句子字符串
def replace_words(dictionary: list, sentence: str) -> str:
    dictionary.sort(key=len)
  
    words = sentence.split()
    for i, word in enumerate(words):
        for root in dictionary:
            if word.startswith(root):
                words[i] = root
                break
    return ' '.join(words)

第六题,这个题木要注意只有能够替换成为目标的时候才能输出为一个true,一开始就是的,那会输出为false并且没有的话就输出为null

# 定义一个使用单词表进行初始化的数据结构类 MagicDictionary,用于判断能否将一个单词中的一个字母替换成另一个字母,使得所形成的新单词已经在构建的单词表中
class MagicDictionary:
    def __init__(self):
        self.words = set() 
        self.counts = {} 

    def buildDict(self, dictionary: list) -> None:
       
        for word in dictionary:
            self.words.add(word) 
            for i, char in enumerate(word): 
                if i not in self.counts:
                    self.counts[i] = {} 
                self.counts[i][char] = self.counts[i].get(char, 0) + 1 

    def search(self, searchWord: str) -> bool:
        
        if searchWord in self.words: 
            return False 
        for i, char in enumerate(searchWord): 
            if i in self.counts: 
                diff = len(self.counts[i]) - self.counts[i].get(char, 0) 
                if diff > 0: 
                    return True 
        return False 

写得我快麻木了这几天,说句心理话,感觉这python真没有c语言写得舒服,感觉python得代码确实简单,但是也确实不太爽。不过还是写完了,心情舒畅。我还想再去了解一下笔记中用到字典树的那个AC自动机算法,看看能不能让我眼前一亮,还有其他的文中提到的算法,我也要去看看,说不定我能自己找出自己的算法,嘿嘿!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值