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

Kimi LeetCode 3455. 最短匹配子字符串 Python3实现

LeetCode 3455. 最短匹配子字符串 — Python3 实现

题目概述

- 给定字符串 `s` 和模式串 `p`,`p` 中恰好包含两个 `'*'`。
- `'*'` 匹配零个或多个任意字符。
- 求 `s` 中匹配 `p` 的最短子串长度,不存在则返回 `-1`。
- 空子串是合法的(例如 `p = "**"` 时答案为 `0`)。

核心思路

将 `p` 按 `'*'` 分割为三部分:`left * mid * right`。

我们需要在 `s` 中找到:
1. `left` 的某个出现位置 `i`
2. `mid` 的某个出现位置 `j`(在 `left` 结束之后)
3. `right` 的某个出现位置 `k`(在 `mid` 结束之后)

使得总长度 `k + len(right) - i` 最小。

方法:KMP + 双指针 / 二分查找

步骤:
1. 用 KMP 找出 `left`、`mid`、`right` 在 `s` 中所有出现位置。
2. 对于 `mid` 的每个出现位置,用双指针或二分查找确定最优的 `left` 和 `right`。
3. 空模式串特殊处理:若某部分为空,则认为每个位置都匹配(返回 `0, 1, 2, ..., n`)。

复杂度
- 时间:O(|s| + |p|),KMP 线性,双指针扫描线性。
- 空间:O(|s| + |p|)。

---

Python3 代码(KMP + 双指针 O(N) 版本)

```python
import math
from typing import List

class Solution:
def shortestMatchingSubstring(self, s: str, p: str) -> int:
# 1. 分割模式串
left, mid, right = p.split('*')
n = len(s)

# 2. KMP 找出所有出现位置
pos_left = self.kmp_all(s, left)
pos_mid = self.kmp_all(s, mid)
pos_right = self.kmp_all(s, right)

# 3. 双指针找最短匹配
ans = math.inf
j = 0 # pos_left 的指针
k = 0 # pos_right 的指针

for i2 in pos_mid:
# left 必须在 mid 开始之前结束:i1 + len(left) <= i2
# 找满足条件的最后一个 left(越靠右越好,缩短总长度)
while j < len(pos_left) and pos_left[j] + len(left) <= i2:
j += 1

if j == 0:
continue # 没有合法的 left

i1 = pos_left[j - 1] # 最靠右的合法 left

# right 必须在 mid 结束之后开始:i3 >= i2 + len(mid)
# 找满足条件的第一个 right(越靠左越好)
while k < len(pos_right) and pos_right[k] < i2 + len(mid):
k += 1

if k == len(pos_right):
break # 后面的 mid 只会更大,right 更不可能满足

i3 = pos_right[k]

# 计算子串长度:[i1, i3 + len(right))
length = i3 + len(right) - i1
ans = min(ans, length)

return -1 if ans == math.inf else ans

def kmp_all(self, text: str, pattern: str) -> List[int]:
"""
返回 pattern 在 text 中所有匹配的起始位置。
如果 pattern 为空,返回 [0, 1, ..., len(text)](每个位置都视为匹配)
"""
if not pattern:
return list(range(len(text) + 1))

# 计算前缀函数 (LPS / PMT)
m = len(pattern)
lps = [0] * m
j = 0
for i in range(1, m):
while j > 0 and pattern[i] != pattern[j]:
j = lps[j - 1]
if pattern[i] == pattern[j]:
j += 1
lps[i] = j

# KMP 匹配
res = []
j = 0
for i in range(len(text)):
while j > 0 and text[i] != pattern[j]:
j = lps[j - 1]
if text[i] == pattern[j]:
j += 1
if j == m:
res.append(i - m + 1)
j = lps[j - 1]

return res
```

---

代码说明

部分 作用
`kmp_all` 标准 KMP 算法,返回所有匹配起始位置。空模式串返回 `0..n`,这是处理 `p = "**"` 等边界情况的关键技巧。
`pos_left / pos_mid / pos_right` 三部分在 `s` 中的所有出现位置列表,天然有序。
双指针循环 枚举 `mid` 的每个位置,用 `j` 找最靠右的合法 `left`,用 `k` 找最靠左的合法 `right`,两者都是单调移动,总复杂度 O(N)。
长度计算 `i3 + len(right) - i1`,即子串右端点(不包含)减去左端点。

---

验证示例

输入 输出 说明
`s="abaacbaecebce", p="ba*c*ce"` `8` 匹配 `"baecebce"`
`s="baccbaadbc", p="cc*baa*adb"` `-1` 无匹配
`s="a", p="**"` `0` 空串匹配
`s="madlogic", p="*adlogi*"` `6` 匹配 `"adlogi"`
`s="aa", p="aa**"` `2` `left="aa"`, `mid=""`, `right=""`,匹配 `"aa"`

---

参考来源

- 题解参考了 KMP + 双指针的 O(N) 实现思路
- 哞靠靠博客中对空模式串的处理技巧(返回 `N+1` 个索引)

http://www.jsqmd.com/news/1107023/

相关文章:

  • 配电房环境监控系统解决方案:电力监护,实现安全运行
  • 面试必考!LLM幻觉检测终极指南:HALLUGUARD+FaithLens+MIT多模型互检,2026最新防幻觉体系
  • PEDOT导电膜卷对卷量产工艺
  • 2026临汾国省考+事业单位一年无限学机构TOP5红黑榜:选错真的耽误一年
  • 品牌在 AI 回答里“掉线“了吗?——全天候 GEO 监测与竞品攻防指南
  • AI 自动写作覆盖自媒体,四成团队已落地流程
  • 公证亲属关系要什么材料?公证亲属关系多久办好?
  • 懂事的 Agent 已经开始自己看屏幕干活了,效率起飞!
  • 顾家童锁净水器,以技术筑起安全防线
  • 终极教程:如何用Platinum-MD让老款索尼MiniDisc播放器重获新生
  • 【声光热力电磁都能做计算】物理具身计算机器人
  • 然后用上面的API测试数据运行下看下效果,发现构建出来的树完全符合我们的预期:
  • vlan技术
  • 深度学习工程实战:从数据清洗到模型部署的决策链
  • ETL 全链路数据污染与逻辑错误定位实战经验分享
  • 上海螺杆泵哪家好?从工程选型角度看靠谱厂家应该具备哪些能力|上诚泵阀
  • 一次服务器被入侵的处理过程分享
  • 【课程设计/毕业设计】基于 Web 的全天候健康传感监督记录系统的设计与实现【附源码、数据库、万字文档】
  • 2026年常德种植牙性价比大比拼,哪家更值得信赖?
  • 跨平台存储革命:如何在Windows上解锁Linux Btrfs文件系统的全部潜能
  • 生命涌现的小龙虾技能之【中医体质识别分析工具】舌诊和面诊在JSVClaw的使用教程
  • 零成本解锁全能AI助手:Codex++接入Agnes免费全模态API完全指南(免费生成图片、视频)
  • 制造业集团数字化转型,标签打印软件国产化替代优先落地思路
  • 好用还专业!2026年最值得拥有的专业降AIGC网站
  • 洛谷 P10113:[GESP202312 八级] 大量的工作沟通 ← 树链剖分 + 链式前向星
  • 2026年主流AI聚合API中转站平台深度测评:从性能压测到企业级选型复盘
  • Java虚拟线程实战:Project Loom让并发编程更简单
  • 厨房电热水器出海:初创品牌如何用轻量化海外客服破解复杂售后难题
  • AI伦理与算法偏见:从概念到工程化治理实践
  • 针对测试的AIAgent开发