当前位置: 首页 > news >正文

字符串匹配经典问题整理

KMP算法

1、找出字符串中第一个匹配项的下标

class Solution:def strStr(self, s: str, pattern: str) -> int:if len(pattern) == 0:return 0ne = [0] * len(pattern)ne[0], k = -1, 0for i in range(2, len(pattern)):while k != 0 and pattern[k] != pattern[i -1]:k = ne[k] if pattern[i - 1] == pattern[k]:ne[i] = k + 1k += 1j = 0for i in range(len(s)):while j > 0 and pattern[j] != s[i]:j = ne[j]if s[i] == pattern[j]:j += 1if j == len(pattern):return i - j  + 1return -1

2、找出数组中的美丽下标 II  -- 匹配多个下标

 class Solution:def beautifulIndices(self, s: str, a: str, b: str, k: int) -> List[int]:def kmp(s, pattern):if len(pattern) == 0:return 0ne = [0] * len(pattern)ne[0], k = -1, 0for i in range(2, len(pattern)):while k != 0 and pattern[k] != pattern[i -1]:k = ne[k] if pattern[i - 1] == pattern[k]:ne[i] = k + 1k += 1j, res = 0, []for i in range(len(s)):while j > 0 and pattern[j] != s[i]:j = ne[j]if s[i] == pattern[j]:j += 1if j == len(pattern):res.append(i - j + 1)j = j - 1j = ne[j]while j > 0 and pattern[j] != s[i]:j = ne[j]if s[i] == pattern[j]:j += 1return resa1, b1 = kmp(s, a), kmp(s, b)res = []for idx in a1:left = bisect_left(b1, idx - k)right = bisect_right(b1, idx + k) - 1if left <= right and left < len(b1):res.append(idx)return res

3、将单词恢复初始状态所需的最短时间 II  -- 自身匹配

class Solution:def minimumTimeToInitialState(self, word: str, kk: int) -> int:def kmp(needle):if len(needle) == 0:return 0ne = [0] * len(needle)ne[0], k = -1, 0for i in range(2, len(needle)):while k != 0 and needle[k] != needle[i -1]:k = ne[k] if needle[i - 1] == needle[k]:ne[i] = k + 1k += 1j, i, res = len(needle), len(needle) - 1, -1while j > 0:if len(needle) - j > 0 and (len(needle) - j) % kk == 0:return (len(needle) - j) // kkj = j - 1j = ne[j]while j > 0 and needle[j] != needle[i]:j = ne[j]if needle[i] == needle[j]:j += 1return resres = kmp(word)if res == -1:return len(word) // kk + (len(word) % kk > 0)return res

 4、kmp算法不适合匹配通配符,例如替换字符后匹配,正确解法是暴力匹配算法:

class Solution:def matchReplacement(self, s: str, sub: str, m: List[List[str]]) -> bool:d = defaultdict(set)for i, j in m:d[i].add(j)for i in range(len(s) - len(sub) + 1):for j in range(len(sub)):if s[i + j] != sub[j] and s[i + j] not in d[sub[j]]:breakelse:return Truereturn False

5、树形kmp --  二叉树中的链表

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:def isSubPath(self, head: ListNode, root: TreeNode) -> bool:        p, k = head.next, headhead.last = Nonewhile p:p.last = headp = p.nextp = head.nextwhile p and p.next:while k != head and k.val != p.val:k = k.last if k.val == p.val:p.next.last = k.nextk = k.nextp = p.next@cachedef dfs(p, q):if not q: return Trueif not p: return Falseres = Falseif p.val == q.val:return dfs(p.left, q.next) or dfs(p.right, q.next)if res: return Trueif q.last: res = dfs(p, q.last) or dfs(p, q.last)if res: return Truereturn dfs(p.left, head) or dfs(p.right, head)if not root: return Falsereturn dfs(root, head)

6、无限重复kmp算法 -- 最大重复子字符串

class Solution:def maxRepeating(self, sequence: str, word: str) -> int:ne = defaultdict(int)def build():ne[0], k, j = -1, 0, 2c = 1while True:i = j % len(word)while k != 0 and word[k % len(word)] != word[i -1]:k = ne[k] if word[i - 1] == word[k % len(word)]:ne[j] = k + 1k += 1j += 1if j % len(word) == 0: yield cc += 1m = build()c = next(m)j, maxl = 0, 0for i in range(len(sequence)):while j > 0 and word[j % len(word)] != sequence[i]:j = ne[j]if sequence[i] == word[j % len(word)]:j += 1if j % len(word) == 0:maxl = max(maxl, j // len(word))if maxl >= c: c = next(m)return maxl

7、重复的子字符串

class Solution:def repeatedSubstringPattern(self, s: str) -> bool:return s in (s+s)[1:-1]

8、旋转字符串

class Solution(object):def rotateString(self, A, B):return len(A) == len(B) and B in A+A

9、kmp+ 数位dp -- 找到所有好字符串

class Solution:def findGoodStrings(self, n: int, s1: str, s2: str, evil: str) -> int:ne = [0] * len(evil)ne[0], k = -1, 0for i in range(2, len(evil)):while k != 0 and evil[k] != evil[i -1]:k = ne[k] if evil[i - 1] == evil[k]:ne[i] = k + 1k += 1@cachedef f(s, i: int, is_limit: bool, j) -> int:if j == len(evil): return 0if i == n: return 1res = 0up = (ord(s[i]) - ord('a')) if is_limit else 25for d in range(0, up + 1):  # 枚举要填入的数字 dc = chr(d + ord('a'))nj = jwhile nj > 0 and evil[nj] != c:nj = ne[nj]if evil[nj] == c:nj += 1res += f(s, i + 1, is_limit and d == up, nj)return res % (10 ** 9 + 7)return (f(s2, 0, True, 0) - f(s1, 0, True, 0) + (1 if evil not in s1 else 0)) % (10 ** 9 + 7)

10、最长快乐前缀

class Solution:def longestPrefix(self, s: str) -> str:n = len(s)fail = [-1] * nfor i in range(1, n):j = fail[i - 1]while j != -1 and s[j + 1] != s[i]:j = fail[j]if s[j + 1] == s[i]:fail[i] = j + 1return s[:fail[-1] + 1]

11、删除一个字符串中所有出现的给定子字符串

class Solution:def removeOccurrences(self, s: str, part: str) -> str:m = len(part)pi1 = [0] * m   # part 的前缀数组# 更新 part 的前缀数组j = 0for i in range(1, m):while j > 0 and part[i] != part[j]:j = pi1[j-1]if part[i] == part[j]:j += 1pi1[i] = jres = []pi2 = [0]   # res 的前缀数组for ch in s:# 模拟从左至右匹配的过程res.append(ch)# 更新 res 的前缀数组j = pi2[-1]while j > 0 and ch != part[j]:j = pi1[j-1]if ch == part[j]:j += 1pi2.append(j)if j == m:# 如果匹配成功,那么删去对应后缀pi2[-m:] = []res[-m:] = []return "".join(res)

12、扩展kmp(z函数)-- 构造字符串的总得分和

class Solution:def sumScores(self, s: str) -> int:n = len(s)z = [0] * nans, l, r = n, 0, 0for i in range(1, n):z[i] = max(min(z[i - l], r - i + 1), 0)  while i + z[i] < n and s[z[i]] == s[i + z[i]]:l, r = i, i + z[i]z[i] += 1ans += z[i]return ans

字典树 

1、实现 Trie (前缀树)

class Trie:def __init__(self):self.children = [None] * 26self.isEnd = Falsedef insert(self, word: str) -> None:node = selffor ch in word:ch = ord(ch) - ord("a")if not node.children[ch]:node.children[ch] = Trie()node = node.children[ch]node.isEnd = Truedef searchPrefix(self, prefix:str):node = selffor ch in prefix:ch = ord(ch) - ord("a")if not node.children[ch]:return Nonenode = node.children[ch]return nodedef search(self, word: str) -> bool:node = self.searchPrefix(word)return node is not None and node.isEnddef startsWith(self, prefix: str) -> bool:return self.searchPrefix(prefix) is not None

2、添加与搜索单词 - 数据结构设计

class WordDictionary:def __init__(self):"""Initialize your data structure here."""self.isEnd = Falseself.ch = [None] * 26def addWord(self, word: str) -> None:p = selffor c in word:c = ord(c) - ord('a')if not p.ch[c]:p.ch[c] = WordDictionary()p = p.ch[c]p.isEnd = True def search(self, word: str) -> bool:return self.searchSub(self, word)def searchSub(self, p, word):for i, c in enumerate(word):if c == '.':    res = Falsefor a in p.ch:if a: res = res or self.searchSub(a, word[i + 1:])return reselse:c = ord(c) - ord('a')if not p.ch[c]:return Falsep = p.ch[c]return p.isEnd# Your WordDictionary object will be instantiated and called as such:
# obj = WordDictionary()
# obj.addWord(word)
# param_2 = obj.search(word)

3、统计前后缀下标对 II  -- 双字符字典树

class Trie:def __init__(self):self.child = {}self.count = 0def add(self, cc):if cc not in self.child:self.child[cc] = Trie()self.child[cc].count += 1return self.child[cc]def get(self, cc):if cc not in self.child:return Nonereturn self.child[cc] class Solution:def countPrefixSuffixPairs(self, words: List[str]) -> int:n = len(words)root = Trie()res = 0for i in reversed(range(n)):p = rootfor j, c in enumerate(words[i]):cc = c + words[i][-j - 1]if not p: breakp = p.get(cc)if p: res += p.countp = rootfor j, c in enumerate(words[i]):p = p.add(c + words[i][-j - 1])return res

4、含最多 K 个可整除元素的子数组

class Solution:def countDistinct(self, nums: List[int], k: int, p: int) -> int:ne = [{}]def get():ne.append({})return len(ne) - 1for i in range(len(nums)):cnt, node = 0, 0for j in range(i, len(nums)):if nums[j] % p == 0: cnt += 1if cnt > k: breakif nums[j] not in ne[node]:ne[node][nums[j]] = get()node = ne[node][nums[j]]return len(ne) - 1

 5、数组中两个数的最大异或值  -- 异或反向匹配

class Tire:def __init__(self):self.ch = [None] * 2def build(self, nums):for num in nums:p = selffor i in range(31, -1, -1):t = (num >> i) & 1if not p.ch[t]:p.ch[t] = Tire()p = p.ch[t]class Solution:def findMaximumXOR(self, nums: List[int]) -> int:t = Tire()t.build(nums)max_n = 0for num in nums:p = txor = 0for i in range(31, -1, -1):n = (num >> i) & 1m = not nif not p.ch[m]:p = p.ch[n]xor = xor << 1else:p = p.ch[m]xor = xor << 1 | 1max_n = max(max_n, xor)return max_n

 字符串哈希

1、判断 DFS 字符串是否是回文串

P = 31
MOD = 10 ** 9 + 7class Solution:def findAnswer(self, parent: List[int], s: str) -> List[bool]:m = defaultdict(list)for i, p in enumerate(parent):if p > -1:m[p].append(i)res = [False] * len(parent)def dfs(p):r1, r2, retf, retb, le = "", "", 0, 0, 0for k in m[p]:t1, t2, f, b, l = dfs(k)r1 += t1r2 = t2 + r2retf = (retf * pow(P, l, MOD) + f) % MODretb = (retb + b * pow(P, le, MOD)) % MODle += lelse:r1 += s[p]r2 = s[p] + r2retf = (retf * pow(P, 1, MOD) + (ord(s[p]) - ord('a'))) % MODretb = (retb + (ord(s[p]) - ord('a')) * pow(P, le, MOD)) % MODle += 1res[p] = (retf == retb and r1 == r2)return r1, r2, retf, retb, ledfs(0)return res

 字符串下标索引

1、用特殊操作处理字符串 II

class Solution:def processStr(self, s: str, k: int) -> str:n = len(s)size = [0] * nsz = 0for i, c in enumerate(s):if c == '*':sz = max(sz - 1, 0)elif c == '#':sz *= 2elif c != '%':  # c 是字母sz += 1size[i] = szif k >= size[-1]:  # 下标越界return '.'# 迭代for i in range(n - 1, -1, -1):c = s[i]sz = size[i]if c == '#':if k >= sz // 2:  # k 在复制后的右半边k -= sz // 2elif c == '%':k = sz - 1 - k  # 反转前的下标为 sz-1-k 的字母就是答案elif c != '*' and k == sz - 1:  # 找到答案return c

http://www.dtcms.com/a/296573.html

相关文章:

  • 深度分析Java内存回收机制
  • DGMR压缩技术:让大规模视觉Transformer模型体积减半而性能不减
  • 数据库第5章期末复习(仅供参考哦)
  • MDO7350A示波器的介绍【PINTECH品致】
  • 如何把Excel文件导入Navicat?
  • EXCEL——INDEX和MATCH傻傻分不清?
  • matplotlib.pyplot: 底层原理简析与进阶技巧
  • 2025暑期—07深度学习应用-YOLO
  • 如何查看docker实例是否挂载目录,以及挂载了哪些目录
  • TLSF(Two-Level Segregated Fit)内存分配器深入解析
  • 力扣.26删除有序数组中的重复项力扣121.买卖人股票的最佳时机力扣.1143最长公共子序列力扣72.编辑距离力扣12.整数转罗马数字
  • 同花顺前端潜在面试题目与答案
  • Redis的Pipeline
  • 期货交易系统:市场生态中的功能映射与价值逻辑
  • VB解除excel保护工作表
  • VTK开发day2:切片矩阵
  • 量子威胁下的区块链进化:后量子密码学时代的分布式账本革命
  • linux-process
  • 跨境支付入门~国际支付结算(基础篇)
  • QT开发---字符编码与QString和QByteArray
  • 窗选和叉选
  • Linux C 网络基础编程
  • 财务数字化——解读集团企业财务共享业务蓝图规划方案【附全文阅读】
  • OpenHarmony中.cfg引导启动配置文件中不同jobs配置项启动顺序
  • MBPO 算法:让智能体像人一样 “先模拟后实操”—强化学习(17)
  • 构建企业级Docker日志驱动:将容器日志无缝发送到腾讯云CLS
  • 《AI流程编排中的Graph观测:设计原理与集成实践》
  • 网卡配置网卡ip和经过网关的ip
  • PAT 甲级题目讲解:1003《Emergency》
  • JavaSE:对一门面向对象语言有一个初步认识