KMP算法(next数组方法)

KMP算法之前需要说一点串的问题:

字符串:ASCII码为基本数据形成的一堆线性结构。

串是一个线性结构;它的存储形式:

typedef struct STRING {

  CHARACTER *head;

  int length;

};

朴素的串匹配算法:

设文本串text = "ababcabcacbab",模式串为patten = "abcac"     其匹配过程如下图所示。

黑色线条代表匹配位置,红色斜杠代表失配位置

算法说明:

一般匹配字符串时,我们从目标字符串text(假设长度为n)的第一个下标选取和patten长度(长度为m)一样的子字符串进行比较,如果一样,就返回开始处的下标值,不一样,选取text下一个下标,同样选取长度为n的字符串进行比较,直到str的末尾(实际比较时,下标移动到n-m)。在普通的匹配中,假如从文本串的第i个字符来开始于模式串匹配。当匹配到模式串的第j位发现失配,即text[i+j] != patten[j]的时候,我们又从文本串的第i+1个位置来重新开始匹配。尽管我们已经知道了好多字符其实根本就匹配不上,我们还是进行了这个过程,这个时候回溯的过程会非常耗费我们的时间。这样的时间复杂度是O(n*m)

代码如下:

int search(const char*str,const char *subStr) {
    int strlen = strlen(str);
    int subStrlen = strlen(subStr);
    int i;
    int j;
    for(i = 0;i <= strlen - subStrlen;i++){
        for(j = 0;j < subStrlen;j++){
            if(str[i + j] != subStr[j])
            break;
        }判断subStrlen是否比较完成
    }
}

KMP算法:

而KMP算法的实质就是,当遇到text[i+j] != patten[j]的时候,但是我们知道模式串中的 0~j-1 位置上的字符已经于i ~ i+j-1位置上的字符是完全匹配的。就不再重新从text[i+1]开始匹配,而是根据next数组的下标找到patten的下标,从那个下标开始匹配。从而时间复杂度为O(m+n)。

例如模式串Patten = "abaabcac"。其next数组如图所示:

我们可以看到这次的匹配在蓝色的c失配了,而c的下标为5,他的next数组的下标为2。因此,下次的匹配不再是从text[1]开始,而是从text[2]开始,这样就省去了不必要的比较。

代码如下:

#include <stdio.h>
#include <malloc.h>
#include <string.h>

#include "kmpmec.h"

void getNext(const char *str, int *next);
int KMPSearch(const char *str, const char *subStr);

int KMPSearch(const char *str, const char *subStr) {
    int strLen = strlen(str);
    int subLen = strlen(subStr);
    int *next;
    int i = 0;
    int j = 0;

    if (strLen <= 0 || subLen <= 0 || strLen < subLen) {
        return -1;
    }
    next = (int *) calloc(sizeof(int), subLen);
    if (subLen > 2) {
        getNext(subStr, next);
    }

    while (strLen - i + next[j] >= subLen) {
        while (subStr[j] != 0 && str[i] == subStr[j]) {
            i++;
            j++;
        }
        if (subStr[j] == 0) {
            free(next);
            return i - subLen;
        } else if (j == 0) {
            i++;
            j = 0;
        } else {
            j = next[j];
        }
    }

    free(next);
    return -1;
}

void getNext(const char *str, int *next) {  //得到next数组
    int i = 2;
    int j = 0;
    boolean flag;

    next[0] = next[1] = 0;  //next数组的前两个下标一定为零
    for (i = 2; str[i]; i++) {
        for (flag = TRUE; flag;) {
            if (str[i-1] == str[j]) { //通过比较失配位置的前一个和前一个下标元素的比较,获取next数组的下标。
                next[i] = ++j;
                flag = FALSE;
            } else if (j == 0) {
                next[i] = 0;
                flag = FALSE;
            } else {
                j = next[j];
            }
        }
    }
}

int main(void) {
    char str[80];
    char subStr[80];
    int result;

    printf("请输入源字符串:");
    gets(str);
    printf("请输入子字符串:");
    gets(subStr);

    result = KMPSearch(str, subStr);
    if (result == -1) {
        printf("字符串[%s]不存在子串[%s]\n", str, subStr);
    } else {
        printf("子串[%s]第一次出现在字符串[%s]中的下标为%d\n", subStr, str, result);
    }

    return 0;
};

原文地址:https://www.cnblogs.com/youdiaodaxue16/p/9665380.html

时间: 2024-11-05 06:04:12

KMP算法(next数组方法)的相关文章

KMP算法&amp;next数组总结

http://www.cnblogs.com/yjiyjige/p/3263858.html KMP算法应该是每一本<数据结构>书都会讲的,算是知名度最高的算法之一了,但很可惜,我大二那年压根就没看懂过~~~ 之后也在很多地方也都经常看到讲解KMP算法的文章,看久了好像也知道是怎么一回事,但总感觉有些地方自己还是没有完全懂明白.这两天花了点时间总结一下,有点小体会,我希望可以通过我自己的语言来把这个算法的一些细节梳理清楚,也算是考验一下自己有真正理解这个算法. 什么是KMP算法: KMP是三位

poj1961(kmp算法next数组应用)

题目链接:https://vjudge.net/problem/POJ-1961 题意:给定一个长为n的字符串(n<=1e6),对于下标i(2<=i<=n),如果子串s(1...i)是周期子串,输出其最大周期. 思路: 考察对kmp算法中next数组的定义掌握,如果(i+1)%(i-j)==0 && (i+1)/(i-j) > 1,那么该子串即为满足条件. AC代码: #include<cstdio> #include<algorithm>

POJ-2752(KMP算法+前缀数组的应用)

Seek the Name, Seek the Fame POJ-2752 本题使用的算法还是KMP 最主要的片段就是前缀数组pi的理解,这里要求解的纸盒pi[n-1]有关,但是还是需要使用一个循环来依次找到前面符合的前缀(所谓符合就是可以保持既是前缀也是s的后缀的子串长度). #include<iostream> #include<cstdio> #include<string> #include<algorithm> #include<set>

A - Number Sequence HDU1005 ( kmp 算法+整数数组的应用)

A - Number Sequence Time Limit:5000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Status Description Given two sequences of numbers : a[1], a[2], ...... , a[N], and b[1], b[2], ...... , b[M] (1 <= M <= 10000, 1 <= N <= 100000

HDU 3336 Count the string(KMP算法next数组的应用)

解题思路: 求前缀数组出现的次数之和,next[i] > 0 表示长度为next[i]的前缀又出现了一次. #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <algorithm> #include <cmath> #include <vector> #include <queue> #

poj2406(求字符串的周期,kmp算法next数组的应用)

题目链接:https://vjudge.net/problem/POJ-2406 题意:求出给定字符串的周期,和poj1961类似. 思路:直接利用next数组的定义即可,当没有周期时,周期即为1. AC代码: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1e6+5; int n,nex[maxn],len; char s[maxn]

poj2185(kmp算法next数组求最小循环节,思维)

题目链接:https://vjudge.net/problem/POJ-2185 题意:给定由大写字母组成的r×c矩阵,求最小子矩阵使得该子矩阵能组成这个大矩阵,但并不要求小矩阵刚好组成大矩阵,即边界部分可以空缺(见样例). 思路: 把每一行视作一个字符,然后对r行求next数组,那么r-nex[r]即为以行为单元的最小循环节大小ans1. 把每一列视作一个字符,然后对c列求next数组,那么c-nex[c]即为以列为单元的最小循环节大小ans2. 最终答案即ans1*ans2.(子矩阵的面积)

KMP算法——next数组求法

j 1 2 3 4 5 6 7 8 9 10 模式串 a b c a b c a c a b 部分匹配值 0 0 0 0 1 2 3 4 0 1 next[] 0 1 1 1 2 3 4 5 1 2 "部分匹配值"是"前缀"和"后缀"的最长的共有元素的长度.以"ABCDABD"为例, - "A"的前缀和后缀都为空集,共有元素的长度为0:   - "AB"的前缀为[A],后缀为[B],共有

hdoj3336(kmp算法next数组的应用)

题目链接:https://vjudge.net/problem/HDU-3336 题意:给定长为n(<=2e5)的字符串s,求s的每个前缀在s中出现的次数之和. 思路: 用dp[i]表示以s[i]为结尾的子串是s的某一种前缀的方案数,那么dp[i]=dp[nex[i]]+1,因为[nex[i]-(i-nex[i])+1 , nex[i]] 和 [nex[i]+1 , i] 是一样的,所以计算后者时只用把前者+1即可(+1是因为以[1,i]为前缀的个数加一). AC代码: #include<cs

hiho 1015 KMP算法 &amp;&amp; CF 625 B. War of the Corporations

#1015 : KMP算法 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一只河蟹,于是河蟹就向小Hi和小Ho提出了那个经典的问题:“小Hi和小Ho,你们能不能够判断一段文字(原串)里面是不是存在那么一些……特殊……的文字(模式串)?” 小Hi和小Ho仔细思考了一下,觉得只能想到很简单的做法,但是又觉得既然河蟹先生这么说了,就