从0开始学算法——第十二天(KMP算法练习)

写在开头的话

学习了今天的基础知识,让我们来做几道题来练练手吧。如果有不懂的可以看看写在文末的思路讲解(题目是别的地方扒来的,参考答案是我自己写的,肯定不是最优解,有更好的方法欢迎评论区交流)

第一题------KMP匹配字符串

第二题------最小循环覆盖长度

参考答案

第一题参考答案(Python版)

python 复制代码
def kmp_search(pattern, text):
    """
    使用 KMP 算法在 text 中搜索 pattern 的所有出现位置
    返回所有起始下标(从 0 开始)
    """
    n = len(pattern)
    m = len(text)
    
    # 构建部分匹配表(next 数组)
    lps = [0] * n  # lps 表示最长前缀后缀匹配长度
    j = 0  # pattern 的指针
    for i in range(1, n):
        while j > 0 and pattern[i] != pattern[j]:
            j = lps[j - 1]
        if pattern[i] == pattern[j]:
            j += 1
            lps[i] = j
    
    # KMP 搜索
    result = []
    j = 0  # pattern 的指针
    for i in range(m):  # i 是 text 的指针
        while j > 0 and text[i] != pattern[j]:
            j = lps[j - 1]
        if text[i] == pattern[j]:
            j += 1
            if j == n:  # 找到一个完整匹配
                result.append(i - n + 1)
                j = lps[j - 1]  # 继续搜索下一个可能匹配
    return result

def main():
    # 读取输入
    n = int(input().strip())
    pattern = input().strip()
    m = int(input().strip())
    text = input().strip()
    
    # 检查长度是否符合题目要求(虽然输入已经保证,但可以作为健壮性检查)
    if n > m:
        print()
        return
    
    # 使用 KMP 搜索
    positions = kmp_search(pattern, text)
    
    # 输出结果
    if positions:
        print(' '.join(map(str, positions)))
    else:
        print()  # 如果没有匹配,输出空行(题目未明确,但通常如此)

if __name__ == "__main__":
    main()

第二题参考答案(Python版)

python 复制代码
def min_cycle_cover(s):
    n = len(s)
    
    # 计算next数组
    next_arr = [0] * n
    j = 0
    for i in range(1, n):
        while j > 0 and s[i] != s[j]:
            j = next_arr[j - 1]
        if s[i] == s[j]:
            j += 1
        next_arr[i] = j
    
    # 查找最小循环覆盖长度
    # 从n开始递减检查每个可能的长度
    for len_val in range(1, n + 1):
        # 检查前n-len_val个字符是否与从len_val开始的n-len_val个字符相同
        # 这等价于检查next[n]是否至少为n-len_val
        if next_arr[n - 1] >= n - len_val:
            # 进一步验证:对于所有i < n-len_val,s[i]应该等于s[i+len_val]
            # 我们可以通过检查前缀函数来验证
            valid = True
            for i in range(n - len_val):
                if s[i] != s[i + len_val]:
                    valid = False
                    break
            if valid:
                return len_val
    
    return n

def main():
    s = input().strip()
    result = min_cycle_cover(s)
    print(result)

if __name__ == "__main__":
    main()

思路讲解

第一题------KMP匹配字符串

算法说明

  1. 构建部分匹配表(LPS 数组)

    • LPS 数组存储了模式串每个位置的最长相同前缀后缀的长度

    • 用于在匹配失败时跳过不必要的比较

  2. KMP 搜索过程

    • 遍历文本串,与模式串进行比较

    • 当字符匹配时,两个指针都向前移动

    • 当字符不匹配时,根据 LPS 数组回退模式串指针

    • 当模式串完全匹配时,记录起始位置,并继续搜索

  3. 时间复杂度

    • 构建 LPS 数组:O(N)

    • KMP 搜索:O(M)

    • 总复杂度:O(N + M),在本题数据规模下完全可行

第二题------最小循环覆盖长度

我们需要找到最小的 len,使得对于所有 0≤i<n−len,有 s[i]=s[i+len]。这表示字符串从第 len 个字符开始的部分与前 n−len 个字符完全相同。

通过KMP的next数组,我们可以快速检查这一点。具体来说:

  • 计算字符串的next数组

  • 对于每个可能的长度 len,如果 s 的前 n−len 个字符与从 len 开始的 n−len 个字符完全匹配,那么 len 是一个候选

  • 我们需要找到最小的这样的 len

相关推荐
夏鹏今天学习了吗1 天前
【LeetCode热题100(87/100)】最小路径和
算法·leetcode·职场和发展
jacGJ1 天前
记录学习--文件读写
java·前端·学习
哈哈不让取名字1 天前
基于C++的爬虫框架
开发语言·c++·算法
峰顶听歌的鲸鱼1 天前
Kubernetes介绍和部署
运维·笔记·云原生·容器·kubernetes·学习方法
枷锁—sha1 天前
【PortSwigger Academy】SQL 注入绕过登录 (Login Bypass)
数据库·sql·学习·安全·网络安全
魔芋红茶1 天前
Spring Security 学习笔记 2:架构
笔记·学习·spring
Lips6111 天前
2026.1.20力扣刷题笔记
笔记·算法·leetcode
2501_941329721 天前
YOLOv8-LADH马匹检测识别算法详解与实现
算法·yolo·目标跟踪
洛生&1 天前
Planets Queries II(倍增,基环内向森林)
算法
小郭团队1 天前
1_6_五段式SVPWM (传统算法反正切+DPWM2)算法理论与 MATLAB 实现详解
嵌入式硬件·算法·matlab·dsp开发