C语言中实现KMP算法的实例讲解_C 语言

一般的算法为什么这么低效呢?那是因为主串指针回溯情况过多:
主串指针如果不回溯的话,速度就会加快,那我们就会想:
如何让主串指针不回溯?
KMP算法就是解决了这个问题,所以速度变得更快速了。
它是这样子的:
用一个数组:next[] 求得失配时的位置,然后保存下来。
要说清楚KMP算法,可以从朴素的模式匹配算法说起。 
朴素的模式匹配算法比较容易理解,其实现如下   

 int  Index(char  s[],  char  p[],  int  pos)
 {
  int  i,  j,  slen,  plen;
  i  =  pos;
  j  =  0;
  slen  =  strlen(s);
  plen  =  strlen(p);
  while((i  <  slen)  &&  (j  <  plen))
  {
   if((s[i]  ==  p[j]))
   {
    i++;
    j++;
   }
   else
   {
    i  =  i-j+1;
    j  =  0;
   }
  }
  if(j  >=  plen)
  {
   return  (i-plen);
  }
  else
  {
   return  -1;
  }
 }

  
可见,在朴素的模式匹配算法中,当模式中的p[j]与主串中的s[i]不匹配时,需要把主串的指针回溯到i-j+1的地方从新用s[i-j+1]跟p[0]进行匹配比较。KMP算法的想法是,能不能不回溯主串的指针呢?这种想法基于如下事实的:p[j]!=s[i]前,p[0]~p[j-1]跟s[i-j]~s[i-1]是匹配的(这里j>0,也就是说在不匹配前已经有匹配的字符了。否则如果j=0,则主串指针肯定不用回溯,直接向前变成i+1再跟p[0]比较就是了) 
  
p[j]!=s[i]前,p[0]~p[j-1]跟s[i-j]~s[i-1]是匹配的,这说明了什么呢?这说明可以通过分析模式的p[0]~p[j-1]来分析s[i-j]~s[i-1]。如果模式中存在p[0]~p[k-1]=p[j-k]~p[j-1](共k个匹配的字符,且k是满足这个关系的最大值),则可以知道s[i-k]~s[j-1]跟[0]~p[k-1]是匹配的,那么,s[i]只需要跟p[k]进行比较就行了。而这个k是跟主串无关的,只需要分析模式串就可以求出来(这就是普通的教材中next[j]=k这个假设的由来,普通教材中总喜欢假设这个k值已经有了,如果你逻辑思维强还没有什么,不然或多或少会把你卡在这的)。亦即next[j]=k。 
  
如果上述的p[0]~p[k-1]=p[j-k]~p[j-1]串不存在会怎么样呢?这说明p[j]前的串中不存在p[0]...=...p[j-1]的情况,就连p[0]也不等于p[j-1],也就是说p[0]~p[j-1]中所有以p[j-1]为结尾的子串跟模式p都是失配的。基于上面p[0]~p[j-1]=s[i-j]~s[i-1]的事实,可以断定s[i-j]~s[i-1]中所有以s[i-1]结尾的子串跟模式p都是失配,这说明把主串的指针回溯到i-j+1~i-1都是没有必要的,既然没有必要回溯,而s[i]!=p[j],则s[i只能跟p[0]进行比较匹配了。亦即next[j]=0。 
  
特殊情况下,j=0,即s[i]!=p[0],这时不用再用s[i]来跟p中的其它字符比较了,变成用s[i+1]跟p[0]进行比较。为了统一,可以让next[0]=-1。在下一轮的比较中,判断到j=-1的情况时,让i=i+1,j=j+1,自然就形成s[i+1]跟p[0]比较的效果了。  
 
KMP算法实现示例

具体请看如下程序:

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

#define MAX 101

void get_next( int *next,char *a,int la) /*求NEXT[]的值*/
{
   int i=1,j=0 ;
   next[1] = 0 ;

   while ( i <= la) /*核心部分*/
   {
      if( a[i] == a[j] || j == 0 )
      {
        j ++ ;
        i ++ ;
        if( a[i] == a[j])
        next[i] = next[j];
        else
        next[i] = j ;
      }
      else
      j = next[j] ;
   }
}

int str_kmp( int *next, char *A ,char *a, int lA,int la)/* EASY*/
{
   int i,j,k ;
   i = 1 ;
   j = 1 ;
   while ( i<=lA && j <= la )
   {
      if(A[i] == a[j] || j == 0 )
      {
          i ++ ;
          j ++ ;
      }
      else
      j = next[j] ;
   }

   if ( j> la)
   return i-j+1 ;
   else
   return -1 ;
}

int main(void)
{
  int n,k;
  int next[MAX]={0} ;
  int lA=0,la =0 ;
  char A[MAX],a[MAX] ;
  scanf("%s %s",A,a) ;

  lA = strlen(A);
  la = strlen(a);
  for(k=la-1; k>= 0 ;k --)
  a[k+1] = a[k] ;
  for(k=lA-1; k>= 0 ;k --)
  A[k+1] = A[k] ;

  get_next(next,a,la) ;
  k = str_kmp(next,A,a,lA,la);
  if ( -1 == k)
  printf("Not Soulation!!! ");
  else
  printf("%d ",k) ;
  system("pause");

  return 0 ;
}

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索c语言
, 匹配
, 算法
, 字符串
, 字符串匹配
KMP算法
kmp算法c语言、kmp算法c语言代码、kmp算法的c语言程序、kmp算法视频讲解、kmp算法next原理讲解,以便于您获取更多的相关知识。

时间: 2024-08-31 06:15:31

C语言中实现KMP算法的实例讲解_C 语言的相关文章

一些C语言中字符串的算法问题解决实例小结_C 语言

    字符串问题是面试中经常出现的问题,这类问题有很多,难以不一.下面是几道字符串的题目,网上都能找到解答,自己实现了一下,供网友参考.感觉算法重要的是要有正确的思路,实现起来不是问题.自己一定要多思考,这样收获可能会更多一点.         问题1:找两个字符串的最长公共子串.         具体描述,如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,则字符串一称之为字符串二的子串.注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中.请编写一个函数,输入两个字

C 语言插入排序算法及实例代码_C 语言

插入排序是排序算法的一种,它不改变原有的序列(数组),而是创建一个新的序列,在新序列上进行操作. 这里以从小到大排序为例进行讲解. 基本思想及举例说明 插入排序的基本思想是,将元素逐个添加到已经排序好的数组中去,同时要求,插入的元素必须在正确的位置,这样原来排序好的数组是仍然有序的. 在实际使用中,通常是排序整个无序数组,所以把这个无序数组分为两部分排序好的子数组和待插入的元素.第一轮时,将第一个元素作为排序好的子数组,插入第二个元素:第二轮,将前两个元素作为排序好的数组,插入第三个元素.以此类

C++中COM组件初始化方法实例分析_C 语言

本文实例讲述了C++中COM组件初始化方法.分享给大家供大家参考.具体如下: 这里使用BCB 在使用TADOConnect等组件时需要进行初始化 调用接口 : CoInitialize(NULL);//初始化COM套件 CoUninitialize();//释放COM套件 在DLL入口中调用: static bool isCoInitialize = false; //是否是自己进行的初始化 int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned l

Prim(普里姆)算法求最小生成树的思想及C语言实例讲解_C 语言

Prim 算法思想:从任意一顶点 v0 开始选择其最近顶点 v1 构成树 T1,再连接与 T1 最近顶点 v2 构成树 T2, 如此重复直到所有顶点均在所构成树中为止. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以把边上的权值解释为线路的造价.则最小生成树表示使其造价最小的生成树. 构造网的最小生成树必须解决下面两个问题: 1.尽可能选取权值小的边,但不能构成回路: 2.选取n-1条恰当的边以连通n个顶点: MST性质:假设G=(V,

C++编程中使用设计模式中的policy策略模式的实例讲解_C 语言

   在看<C++设计新思维>的时候,发现在一开始就大篇幅的介绍策略模式(policy),策略模式不属于经典设计模式中的一种,但是其实在我们日常的开发中是必不可少的.policy,策略,方针,这里的意思是指把复杂功能的类尽量的拆分为功能单一的简单类的组合,简单的类只负责单纯行为或结构的某一方面.增加程序库的弹性,可复用性,可扩展性.policy是一个虚拟的概念,他定义了某一类class的一些接口规范,并不与C++语法的关键字对应,只是一个抽象的概念. 实例1: //policy模式的常见使用实

C语言函数的递归和调用实例分析_C 语言

一.基本内容: C语言中的函数可以递归调用,即:可以直接(简单递归)或间接(间接递归)地自己调自己. 要点: 1.C语言函数可以递归调用. 2.可以通过直接或间接两种方式调用.目前只讨论直接递归调用. 二.递归条件 采用递归方法来解决问题,必须符合以下三个条件: 1.可以把要解决的问题转化为一个新问题,而这个新的问题的解决方法仍与原来的解决方法相同,只是所处理的对象有规律地递增或递减. 说明:解决问题的方法相同,调用函数的参数每次不同(有规律的递增或递减),如果没有规律也就不能适用递归调用. 2

一波C语言二元查找树算法题目解答实例汇总_C 语言

按层次遍历二元树问题描述:输入一颗二元树,从上往下按层打印树的每个结点,同一层中按照从左往右的顺序打印.  例如输入: 8 / / 6 10 / / / / 5 7 9 11 输出 8 6 10 5 7 9 11           定义二元树(其实是二元搜索树,但并不遍历算法)的结点为: struct BSTreeNode { int value; BSTreeNode *left; BSTreeNode *right; };       思路:利用队列的先进先出,很容易实现.每次取出队列的首

C语言中的函数指针基础学习教程_C 语言

顾名思义,函数指针就是函数的指针.它是一个指针,指向一个函数.看例子: A) char * (*fun1)(char * p1,char * p2); B) char * *fun2(char * p1,char * p2); C) char * fun3(char * p1,char * p2); 看看上面三个表达式分别是什么意思? C)这很容易,fun3是函数名,p1,p2是参数,其类型为char *型,函数的返回值为char *类型. B) 也很简单,与C)表达式相比,唯一不同的就是函数的

c语言中 基于随机函数的使用详解_C 语言

在C语言中,rand()函数可以用来产生随机数,但是这不是真真意义上的随机数,是一个伪随机数,是根据一个数,我们可以称它为种子,为基准以某个递推公式推算出来的一系数,当这系列数很大的时候,就符合正态公布,从而相当于产生了随机数,但这不是真正的随机数,当计算机正常开机后,这个种子的值是定了的,除非你破坏了系统,为了改变这个种子的值,C提供了srand()函数,它的原形是void srand( int a). 可能大家都知道C语言中的随机函数random,可是random函数并不是ANSI C标准,