题目难度: 中等
今天继续更新程序员面试金典系列, 大家在公众号 算法精选 里回复 面试金典 就能看到该系列当前连载的所有文章了, 记得关注哦~
给定一个较长字符串 big 和一个包含较短字符串的数组 smalls,设计一个方法,根据 smalls 中的每一个较短字符串,对 big 进行搜索。输出 smalls 中的字符串在 big 里出现的所有位置 positions,其中 positions[i]为 smalls[i]出现的所有位置。
O(NMS): 假设 N 是 big 字符串的长度, M 是 smalls 字符串数组的元素个数, S 是 small 字符串的平均长度; 则外层循环复杂度为 O(M), 内层循环最多需要对每个起点匹配对应的子串, 就是 O(NS), 所以总时间复杂度是 O(NMS)O(1): 除了结果数组外, 没有使用额外的空间class Solution:
def multiSearch(self, big: str, smalls: List[str]) -> List[List[int]]:
# 方法1: 语言自带find
res = []
for s in smalls:
res.append([])
# 初始化起点为-1, 因为下面的find逻辑是从i+1开始查找
i = -1
if s != "":
while True:
# i是上一个查找到的起点, 接下来从i+1开始查, 保证不重复
i = big.find(s, i + 1)
if i != -1:
# 找到了, 追加起点
res[-1].append(i)
else:
# 没找到, 跳出循环
break
return res
O(MS+NS): 假设 N 是 big 字符串的长度, M 是 smalls 字符串数组的元素个数, S 是 small 字符串的平均长度; 则插入 trie 的复杂度为 O(MS), 查找起点时, 外层循环复杂度为 O(N), 内层最多遍历 S 层, 复杂度为 O(S), 所以这部分总复杂度为 O(NS)O(MS): 需要存储 small 字符串列表的所有字符class Solution:
def multiSearch(self, big: str, smalls: List[str]) -> List[List[int]]:
# 方法2: 逆向思考+trie存small+遍历big起点
class Node:
def __init__(self, c):
self.c = c
# children字典, 存储字符到节点的映射
self.children = {}
# small字符串下标, 初始化为-1, 只有当遍历到small最后一个字符时才赋值, 保证完整性
self.index = -1
n, m = len(big), len(smalls)
root = Node("")
for i, w in enumerate(smalls):
# 将当前字符串加入trie中, 达到终点时记录当前下标
# 因为smalls的字符串都不重复, 所以不存在下标覆盖的情况
cur = root
for c in w:
if c not in cur.children:
cur.children[c] = Node(c)
cur = cur.children[c]
# 赋值下标, 从root到cur的路径形成了完整的当前small字符串
cur.index = i
# 预生成结果列表
res = [[] for _ in range(m)]
for start in range(n):
# 遍历big的每个起点start
cur = root
i = start
while cur and i < n:
c = big[i]
if c not in cur.children:
# 当前字符不在trie中, 后面就更不存在有效的small字符串了, 直接退出循环
break
# 进入下一层
cur = cur.children[c]
i += 1
if cur.index != -1:
# 当前节点索引不是-1, 说明找到了一个完整的small字符串 (从root一路到cur)
# 此时需要将结果列表的small下标处追加上当前big的起点start, 表示这个small字符串在start位置处出现
res[cur.index].append(start)
return res
大家可以在下面这些地方找到我~😊
我的公众号: 算法精选, 欢迎大家扫码关注~😊
