无额外模块下优化大规模字符串多子串重叠出现次数统计方案问询
优化方案
核心耗时原因
你当前的方案是每个待统计子串单独调用find遍历主串,65万次find调用等价于将超大主串重复遍历了数十万次,这是耗时过高的根本原因。
分场景优化方案
场景1:所有待统计子串长度一致
直接滑动窗口单次遍历主串,统计所有对应长度的子串出现次数,仅需遍历主串1次,时间复杂度为O(N)(N为主串长度),实现代码如下:
# 提前收集所有待统计的子串,存为集合用于快速判断 target_temps = set(你所有的待统计子串列表) L = len(next(iter(target_temps))) # 所有子串统一长度 n = len(txt) diz = {} # 单次滑动窗口遍历 for i in range(n - L + 1): current_sub = txt[i:i+L] if current_sub in target_temps: if current_sub not in diz: diz[current_sub] = 0 diz[current_sub] += 1
该方案在超大文本场景下也能轻松将耗时控制在1秒以内。
场景2:待统计子串长度不统一
自行实现轻量Aho-Corasick(AC)自动机,仅需遍历主串1次即可统计所有子串的重叠出现次数,无需重复遍历主串,纯Python实现无需依赖第三方模块,核心实现代码如下:
class ACNode: def __init__(self): self.children = {} self.fail = None self.output = [] # 存储以当前节点结尾的所有子串 def build_automaton(patterns): root = ACNode() # 构建前缀树 for pattern in patterns: node = root for c in pattern: if c not in node.children: node.children[c] = ACNode() node = node.children[c] node.output.append(pattern) # 构建失败指针 queue = [] for child in root.children.values(): child.fail = root queue.append(child) while queue: current_node = queue.pop(0) for c, child in current_node.children.items(): fail_node = current_node.fail while fail_node is not None and c not in fail_node.children: fail_node = fail_node.fail child.fail = fail_node.children[c] if fail_node else root if child.fail: child.output.extend(child.fail.output) queue.append(child) return root def ac_search(root, txt): count_dict = {} current = root for c in txt: while current is not None and c not in current.children: current = current.fail current = current.children[c] if current else root # 累计所有匹配到的子串 for pattern in current.output: if pattern not in count_dict: count_dict[pattern] = 0 count_dict[pattern] += 1 return count_dict # 调用示例 diz = ac_search(build_automaton(你所有的待统计子串列表), txt)
优化效果验证
上述两种方案都仅需遍历主串1次,避免了数十万次find调用带来的重复遍历开销,针对GB级以内的文本和数十万级的子串量,都能将耗时控制在1秒以内,完全满足性能要求。
内容的提问来源于stack exchange,提问作者EddyIT
相关产品推荐
相关产品推荐

