题目重点:KMP状态不中断,匹配可从一片开始在下一片结束。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- fragments保持顺序,视为拼接文本;返回第一个匹配起点的(fragmentIndex,offset),None表示无匹配。
- 按code point计offset;空pattern训练返回(0,0)即逻辑起点,即使无片,调用方可另定空输入语义。
解题路线
- 预处理pattern失败函数,遍历各片字符时保留matched,不因换片归零。失配用prefix回退,不重新从头扫。
- 完成匹配时全局起点=当前全局位置−pattern长度+1;片前缀长度数组与bisect_right将起点映回真实字符所属片。
- 空片产生重复前缀长度,bisect_right跳过零长片,保证非空匹配的起点落在含字符的片内。纯流不保留全部片时可记录最近pattern长度的来源信息。
手动走一遍
["a","bcd","ea","de"]查de:第一次在bcd最后一个d与ea首e跨片匹配,返回(1,2)。["","ab","","c"]查bc返回(1,1),不是空片下标。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
from bisect import bisect_right
def find_fragment(fragments,pattern):
if not pattern:return (0,0)
prefix=[0]*len(pattern)
for i in range(1,len(pattern)):
j=prefix[i-1]
while j and pattern[i]!=pattern[j]:j=prefix[j-1]
if pattern[i]==pattern[j]:j+=1
prefix[i]=j
offsets=[0]
for fragment in fragments:offsets.append(offsets[-1]+len(fragment))
matched=position=0
for fragment in fragments:
for c in fragment:
while matched and c!=pattern[matched]:matched=prefix[matched-1]
if c==pattern[matched]:matched+=1
if matched==len(pattern):
start=position-len(pattern)+1
i=bisect_right(offsets,start)-1
return i,start-offsets[i]
position+=1
return None
复杂度
总扫描字符N、pattern长M、片数F;O(N+M+F)预处理/扫描,最后一次回映O(logF),空间O(M+F)。不拼接整个文本。
易错点与追问
容易失分的边界
- 按每片调用find会漏掉跨片匹配。
- 找最后一次出现或每片最早再比较不是首个全局匹配。
继续深挖
- 要全部重叠匹配,完成后matched应回退到哪?
- 返回起止两片位置并支持真正无界流,需要存多少来源状态?
