KMP 字符串



提示:以下是本篇文章正文内容

一、题目

给定一个字符串 S
,以及一个模式串 P
,所有字符串中只包含大小写英文字母以及阿拉伯数字。

模式串 P
在字符串 S
中多次作为子串出现。

求出模式串 P
在字符串 S
中所有出现的位置的起始下标。

输入格式
第一行输入整数 N
,表示字符串 P
的长度。

第二行输入字符串 P

第三行输入整数 M
,表示字符串 S
的长度。

第四行输入字符串 S

输出格式
共一行,输出所有出现位置的起始下标(下标从 0
开始计数),整数之间用空格隔开。

数据范围
1≤N≤105

1≤M≤106

输入样例:
3
aba
5
ababa
输出样例:
0 2

二、思路及代码

1.思路

本质是寻找子串自身的最长公共前后缀位置,ne数组就是方便不匹配的时候向后退的步数最少。也就是确保每次匹配过程尽可能不再像常规的暴力的方法从模板串的头开始匹配 ,而是选择一个匹配的中间的部分来进行继续对比,如果不匹配继续后退,但是如果匹配就找到了答案。

next[i] = j 含义是
模板串p[1~j] == p[i - j + 1, i] 匹配上。

2.答案

代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1E5 + 10, M = 1E6 + 10;

char s[M], p[N];
int ne[N];

int main()
{
    int n, m;
    
    cin >> n >> p + 1 >> m >> s + 1;
    
    //ne 数组处理过程, 只与子串有关, 其实是寻找最长公共前后缀
    for(int i = 2, j = 0; i <= n; i ++)
    {
        while(j && p[i] != p[j + 1]) j = ne[j];
        if(p[i] == p[j + 1]) j ++;
        ne[i] = j;
    }
    //kmp 匹配过程
    for(int i = 1, j = 0; i <= m; i ++)
    {
        while(j && s[i] != p[j + 1]) j = ne[j];
        if(s[i] == p[j + 1]) j ++;
        if(j == n)// 匹配成功
        {
            printf("%d ", i - n);
            j = ne[j];// 下一次匹配 退一步哦
        }
    }
    
    return 0;
}






总结

Just Review.

### KMP字符串匹配算法的原理 KMP(Knuth-Morris-Pratt)算法是一种高效的字符串匹配算法,它通过构建部分匹配表(通常称为`next数组`),避免了传统暴力匹配中的重复比较操作。以下是KMP算法的核心原理: 1. **前缀和后缀的概念** 在模式串中,定义某个子串的最大公共前后缀长度。例如,在模式串 `aabaa` 中,子串 `aab` 的最大公共前后缀为 `a`,因此它的长度为 1。 2. **部分匹配表(Next 数组)** 部分匹配表记录了模式串中每个字符之前的部分所能达到的最大相同前后缀长度。这个表可以用来指导当发生失配时应该跳转到哪个位置继续匹配[^1]。 3. **减少回溯次数** 当发现当前字符不匹配时,利用已经计算好的 Next 数组来决定下一步从哪里重新开始匹配,而不是像朴素算法那样简单地移动一位并重试整个过程[^4]。 --- ### Python 实现 KMP 算法 下面是基于上述理论的一个完整的 Python 实现示例: #### 构建 Next 数组 ```python def build_next(pattern): next_array = [-1] * len(pattern) if len(pattern) > 0: next_array[0] = -1 k = -1 for q in range(1, len(pattern)): while k >= 0 and pattern[k + 1] != pattern[q]: k = next_array[k] if pattern[k + 1] == pattern[q]: k += 1 next_array[q] = k return next_array ``` #### 主函数实现 ```python def kmp_search(text, pattern): m = len(pattern) n = len(text) # 如果模式为空,则认为找到的位置是0 if m == 0: return 0 next_array = build_next(pattern) q = -1 for i in range(n): while q >= 0 and pattern[q + 1] != text[i]: q = next_array[q] if pattern[q + 1] == text[i]: q += 1 if q == m - 1: return i - m + 1 # 返回匹配起始索引 return -1 # 表示未找到 ``` --- ### Java 实现 KMP 算法 对于 Java 用户来说,也可以采用类似的逻辑实现 KMP 算法。下面是一个简单的例子: #### 构造 Next 数组 ```java public static int[] computeLPSArray(String pat) { int M = pat.length(); int lps[] = new int[M]; int length = 0; lps[0] = 0; int i = 1; while (i < M) { if (pat.charAt(i) == pat.charAt(length)) { length++; lps[i] = length; i++; } else { if (length != 0) { length = lps[length - 1]; } else { lps[i] = 0; i++; } } } return lps; } ``` #### 主函数实现 ```java public static int KMPSearch(String txt, String pat) { int N = txt.length(); int M = pat.length(); int lps[] = computeLPSArray(pat); int i = 0; // index for txt[] int j = 0; // index for pat[] while ((N - i) >= (M - j)) { if (pat.charAt(j) == txt.charAt(i)) { j++; i++; } if (j == M) { return i - j; // Match found at position i-j. } else if (i < N && pat.charAt(j) != txt.charAt(i)) { if (j != 0) { j = lps[j - 1]; } else { i = i + 1; } } } return -1; // No match found. } ``` --- ### 性能分析 相比于传统的暴力匹配方法 O((m-n+1)*n)[^2],KMP 算法的时间复杂度仅为 O(m+n),其中 m 和 n 分别表示文本串和模式串的长度。这是因为 KMP 利用了已有的匹配信息减少了不必要的回溯操作[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值