KMP查找子字符串算法

举例说明:

S:  ababcababa

P:  ababa

KMP算法之所以叫做KMP算法是因为这个算法是由三个人共同提出来的,就取三个人名字的首字母作为该算法的名字。其实KMP算法与BF算法的区别就在于KMP算法巧妙的消除了指针i的回溯问题,只需确定下次匹配j的位置即可,使得问题的复杂度由O(mn)下降到O(m+n)。

  在KMP算法中,为了确定在匹配不成功时,下次匹配时j的位置,引入了next[]数组,next[j]的值表示P[0...j-1]中最长后缀的长度等于相同字符序列的前缀。

  对于next[]数组的定义如下:

 1) next[j] = -1  j = 0

 2) next[j] = max(k): 0<k<j   P[0...k-1]=P[j-k,j-1]

 3) next[j] = 0  其他

 如:

 P      a    b   a    b   a

 j      0    1   2    3   4

next    -1   0   0    1   2

 即next[j]=k>0时,表示P[0...k-1]=P[j-k,j-1]

 因此KMP算法的思想就是:在匹配过程称,若发生不匹配的情况,如果next[j]>=0,则目标串的指针i不变,将模式串的指针j移动到next[j]的位置继续进行匹配;若next[j]=-1,则将i右移1位,并将j置0,继续进行比较。

代码实现如下:

  

int KMPMatch(char *s,char *p)
{
    int next[100];
    int i,j;
    i=0;
    j=0;
    getNext(p,next);
    while(i<strlen(s))
    {
        if(j==-1||s[i]==p[j])
        {
            i++;
            j++;
        }
        else
        {
            j=next[j];       //消除了指针i的回溯
        }
        if(j==strlen(p))
            return i-strlen(p);
    }
    return -1;
}

因此KMP算法的关键在于求算next[]数组的值,即求算模式串每个位置处的最长后缀与前缀相同的长度, 而求算next[]数组的值有两种思路,第一种思路是用递推的思想去求算,还有一种就是直接去求解。

1.按照递推的思想:

根据定义next[0]=-1,假设next[j]=k, 即P[0...k-1]==P[j-k,j-1]

1)若P[j]==P[k],则有P[0..k]==P[j-k,j],很显然,next[j+1]=next[j]+1=k+1;

2)若P[j]!=P[k],则可以把其看做模式匹配的问题,即匹配失败的时候,k值如何移动,显然k=next[k]。

因此可以这样去实现:

void getNext(char *p,int *next)
{
    int j,k;
    next[0]=-1;
    j=0;
    k=-1;
    while(j<strlen(p)-1)
    {
        if(k==-1||p[j]==p[k])    //匹配的情况下,p[j]==p[k]
        {
            j++;
            k++;
            next[j]=k;
        }
        else                   //p[j]!=p[k]
            k=next[k];
    }
}

KMP查找子字符串算法

时间: 2024-10-16 23:56:40

KMP查找子字符串算法的相关文章

在父字符串中查找子字符串

在父字符串中查找子字符串(指针控制,也可选择标控制) #pragma once #include<iostream> #include<assert.h> using namespace std; char* StrStr(char* source, char* dest) { assert(source&&dest); if (strlen(source) < strlen(dest)) return NULL; char* newSrc = NULL; c

[C语言】模拟实现库函数strstr,查找子字符串

//模拟实现库函数strstr,查找子字符串 #include <stdio.h> #include <assert.h> char * my_strstr( char *dst, const char * src) { assert(dst); assert(src); int i, j, k; for (i = 0; dst[i] != '\0'; i++) { for (j = i, k = 0; src[k] != '\0'&&dst[j] == src[k

字符串匹配算法一:查找子字符串

[题目] 就是给一个很长的字符串str 还有一个字符集比如{a,b,c} 找出str里包含{a,b,c}的最短子串.要求O(n). [例子] 字符集是a,b,c,字符串是abdcaabcx,则最短子串为abc. [分析] 有题意可知,满足要求的字符串只需要包括字符集中的所有字符,并没有顺序要求 当然最容易想到的是做一个字符匹配的过程,但题目要求查找次数为O(n),在思考了几种解决方法后,觉得下面的方案能够达到要求,虽然需要一些额外的空间. 下面我给出自己的解决方案,难免有遗漏的地方,如果路过的朋

[Swift]扩展String类:实现find()查找子字符串在父字符串中的位置

类似于C++中的find()函数:比较等于val的范围中第一个元素的迭代器.如果没有元素匹配,则函数最后返回. 返回值是子字符串在父字符串中的位置(下标记录), 如果没有找到,那么会返回一个特别的标记npos(-1). 返回值可以看成是一个Int型的数. 1 //String扩展 2 extension String { 3 // 截取字符串:从index到结束处 4 // - Parameter index: 开始索引 5 // - Returns: 子字符串 6 func subString

查找子字符串的算法

#include<stdio.h> int main() { char *str1; char *str2; char *ptr=str1; char *p1=NULL; char *p2=NULL; while(*ptr) { p1=ptr; p2=str2;                        /*保证str1和str2的首地址不变*/ while((*p1=*p2)&&(*p1))      /*保证p1已经遍历到了'\0'*/ { p1++; p2++; if

G - Number Sequence( 查找子字符串 )

Given two sequences of numbers : a11, a22, ...... , aNN, and b11, b22, ...... , bMM (1 <= M <= 10000, 1 <= N <= 1000000). Your task is to find a number K which make aKK = b11, aK+1K+1 = b22, ...... , aK+M?1K+M?1 = bMM. If there are more than o

strstr查找子字符串函数

strstr() 函数搜索一个字符串在另一个字符串中的第一次出现.该函数返回字符串的其余部分(从匹配点).如果未找到所搜索的字符串,则返回 false.该函数是二进制安全的. //数组 #include <stdio.h>   char *my_strstr(const char *str, const char *sub_str) {     for(int i = 0; str[i] != '\0'; i++)     {         int tem = i; //tem保留主串中的起

c 查找 子字符串

#include <stdio.h> int Search(char *s1, char *s2) { char *temp = s1; int count = 0; while(*temp != '\0') { char *tmp1 = temp; char *t1 = s2; while(*t1 != '\0' && *tmp1 != '\0') { if(*t1 != *tmp1) break; //如果相等,且到最后,返回1 if(*t1 == *tmp1 &&

NSString子字符串查找在父字符串的位置.

NSString上一章我们讲解了如何在父字符串中提取它的一部分作为一个新的子字符串而存在, 今天我们就来讲讲怎么知道子字符串在父字符串中的位置, 以及长度是多少. 这里使用的方法: NSRange: 前面我们说过, NSRange是作为一个结构体一样的存在, 它里面是由两个长整数类型构成的, 而rangOfString:就是把要判断的子字符串在这声明, 由它来返回给NSRange来判断子字符串所在父字符串的位置以及长度. 下面是简单例子: #import <Foundation/Foundati