KMP算法简单介绍

KMP算法最大的难点的难点在于如何处理P数组。
P[i]表示长度为i的字符串中最长且相等的的前缀和后缀,比如abbab中开头的ab和末尾的ab相等,那么p[5]=2。
先给出一段预处理的代码,从代码来分析做法:

void prework(char s[]) // s数组从下标1开始存储字符
{
    int len = strlen(s+1);
    next[1] = 0;
    int j = 0;
    for (int i = 1; i < len; i++)
    {
        //末尾新的字符和上一轮中最长前缀的后一个字符不匹配,j向前移动到相同字符处
        while (j > 0 && s[j + 1] != s[i + 1])
            j = next[j];
        //后退操作后的前缀和末尾字符进行比较
        if (s[j + 1] == s[i + 1])
            j++;
        next[i + 1] = j;
    }
}

其中while循环是比较难理解的一个地方。
abaababa为例,当取出abaabab这个字串进行判断时,j=3,i=6,此时a[j + 1] != a[i + 1]是成立的,需要进行回退,即进行j = p[j]的操作,回退后j=1,a[j + 1] == a[i + 1]成立,退出循环。
这一步的操作是使j回退到上一个相同字符处,因为a[ j ]=a[ p[ j ] ],此时再进行判断后一位字符是否相等,如果还不相等就继续回退,直到出现相等的字符或者j=0(没有符合情况的字符)。
KMP代码:

void KMP(char s1[], char s2[]) // s1和s2从下标1开始存储字符
{
    int len1 = strlen(s1 + 1);
    int len2 = strlen(s2 + 1);
    int j = 0;
    for (int i = 1; i <= len1; i++)
    {
        while (j && s2[j + 1] != s1[i])
            j = next[j];
        if (s2[j + 1] == s1[i]) j++;
        if (j == len2) 
        {
            //如果找到了相同的串,输出s1中起始位置
            std::cout << i - len2 + 1 << std::endl;
            j = next[j];
        }
    }
}

KMP入门题 可重叠字符匹配

#include <bits/stdc++.h>
#define maxn 1000005
using namespace std;

int p[maxn];
char s1[maxn], s2[maxn];
void prework(char *a, int m) //a数组从下标1开始存储字符
{
    p[1] = 0;
    int j = 0;
    for (int i = 1; i < m; i++)
    {
        //末尾新的字符和上一轮中最长前缀的后一个字符不匹配,后退一步
        while (j > 0 && a[j + 1] != a[i + 1])
            j = p[j];
        //后退操作后的前缀和末尾字符进行比较
        if (a[j + 1] == a[i + 1])
            j++;
        p[i + 1] = j;
    }
}

int main()
{
    scanf("%s %s", s1 + 1, s2 + 1);
    int len1 = strlen(s1 + 1), len2 = strlen(s2 + 1);
    prework(s2, len2);
    int i = 0, j = 0;
    //进行匹配
    for (int i = 1; i <= len1;i++)
    {
        //出现不匹配的地方将j回退
        //如果j=0就退出循环
        while(j && s2[j+1]!=s1[i]) 
            j = p[j];
        if(s1[i]==s2[j+1])
            j++;
        if(j==len2)
            printf("%d\n", i - len2 + 1), j = p[j];
    }
    for (int i = 1; i <= len2; i++)
        printf("%d ", p[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值