从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

相关推荐
Math_teacher_fan2 小时前
第二篇:核心几何工具类详解
人工智能·算法
汉克老师2 小时前
CCF-NOI2025第二试题目与解析(第二题、集合(set))
c++·算法·noi·子集卷积·sos dp·mod 异常
TG:@yunlaoda360 云老大2 小时前
腾讯云国际站代理商TACO Kit有什么优势呢?
学习·云计算·腾讯云
Kapibalapikapi2 小时前
工具 | netcat, netstat
网络·笔记·逆向
mit6.8242 小时前
presum|
算法
不穿格子的程序员2 小时前
从零开始写算法——链表篇2:从“回文”到“环形”——链表双指针技巧的深度解析
数据结构·算法·链表·回文链表·环形链表
guygg882 小时前
基于Matlab的压缩感知信道估计算法实现
开发语言·算法·matlab
IMPYLH3 小时前
Lua 的 warn 函数
java·开发语言·笔记·junit·lua