C語(yǔ)言中實(shí)現(xiàn)KMP算法的實(shí)例講解
一般的算法為什么這么低效呢?那是因?yàn)橹鞔羔樆厮萸闆r過(guò)多:
主串指針如果不回溯的話,速度就會(huì)加快,那我們就會(huì)想:
如何讓主串指針不回溯?
KMP算法就是解決了這個(gè)問(wèn)題,所以速度變得更快速了。
它是這樣子的:
用一個(gè)數(shù)組:next[] 求得失配時(shí)的位置,然后保存下來(lái)。
要說(shuō)清楚KMP算法,可以從樸素的模式匹配算法說(shuō)起。
樸素的模式匹配算法比較容易理解,其實(shí)現(xiàn)如下
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;
}
}
可見,在樸素的模式匹配算法中,當(dāng)模式中的p[j]與主串中的s[i]不匹配時(shí),需要把主串的指針回溯到i-j+1的地方從新用s[i-j+1]跟p[0]進(jìn)行匹配比較。KMP算法的想法是,能不能不回溯主串的指針呢?這種想法基于如下事實(shí)的:p[j]!=s[i]前,p[0]~p[j-1]跟s[i-j]~s[i-1]是匹配的(這里j>0,也就是說(shuō)在不匹配前已經(jīng)有匹配的字符了。否則如果j=0,則主串指針肯定不用回溯,直接向前變成i+1再跟p[0]比較就是了)
p[j]!=s[i]前,p[0]~p[j-1]跟s[i-j]~s[i-1]是匹配的,這說(shuō)明了什么呢?這說(shuō)明可以通過(guò)分析模式的p[0]~p[j-1]來(lái)分析s[i-j]~s[i-1]。如果模式中存在p[0]~p[k-1]=p[j-k]~p[j-1](共k個(gè)匹配的字符,且k是滿足這個(gè)關(guān)系的最大值),則可以知道s[i-k]~s[j-1]跟[0]~p[k-1]是匹配的,那么,s[i]只需要跟p[k]進(jìn)行比較就行了。而這個(gè)k是跟主串無(wú)關(guān)的,只需要分析模式串就可以求出來(lái)(這就是普通的教材中next[j]=k這個(gè)假設(shè)的由來(lái),普通教材中總喜歡假設(shè)這個(gè)k值已經(jīng)有了,如果你邏輯思維強(qiáng)還沒有什么,不然或多或少會(huì)把你卡在這的)。亦即next[j]=k。
如果上述的p[0]~p[k-1]=p[j-k]~p[j-1]串不存在會(huì)怎么樣呢?這說(shuō)明p[j]前的串中不存在p[0]...=...p[j-1]的情況,就連p[0]也不等于p[j-1],也就是說(shuō)p[0]~p[j-1]中所有以p[j-1]為結(jié)尾的子串跟模式p都是失配的?;谏厦鎝[0]~p[j-1]=s[i-j]~s[i-1]的事實(shí),可以斷定s[i-j]~s[i-1]中所有以s[i-1]結(jié)尾的子串跟模式p都是失配,這說(shuō)明把主串的指針回溯到i-j+1~i-1都是沒有必要的,既然沒有必要回溯,而s[i]!=p[j],則s[i只能跟p[0]進(jìn)行比較匹配了。亦即next[j]=0。
特殊情況下,j=0,即s[i]!=p[0],這時(shí)不用再用s[i]來(lái)跟p中的其它字符比較了,變成用s[i+1]跟p[0]進(jìn)行比較。為了統(tǒng)一,可以讓next[0]=-1。在下一輪的比較中,判斷到j(luò)=-1的情況時(shí),讓i=i+1,j=j+1,自然就形成s[i+1]跟p[0]比較的效果了。
KMP算法實(shí)現(xiàn)示例
具體請(qǐng)看如下程序:
#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 ;
}
相關(guān)文章
C++編程中逗號(hào)運(yùn)算符和條件運(yùn)算符的使用方法講解
這篇文章主要介紹了C++編程中逗號(hào)運(yùn)算符和條件運(yùn)算符的使用方法講解,需要的朋友可以參考下2016-01-01
ubuntu系統(tǒng)下C++調(diào)用matlab程序的方法詳解
學(xué)習(xí)c++與matlab混合編程一般是通過(guò)c++調(diào)用matlab函數(shù),因?yàn)閙atlab具有強(qiáng)大的數(shù)學(xué)函數(shù)庫(kù),然而vc++具有界面設(shè)計(jì)靈活的優(yōu)點(diǎn),下面這篇文章主要給大家介紹了關(guān)于在ubuntu系統(tǒng)下C++調(diào)用matlab程序的方法,需要的朋友可以參考下。2017-08-08
VSCODE+cmake配置C++開發(fā)環(huán)境的實(shí)現(xiàn)步驟
這篇文章主要介紹了VSCODE+cmake配置C++開發(fā)環(huán)境的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
基于c++強(qiáng)制類型轉(zhuǎn)換的(總結(jié))詳解
本篇文章對(duì)C++中的強(qiáng)制類型轉(zhuǎn)換進(jìn)行了詳細(xì)的分析介紹。需要的朋友參考下2013-05-05
C語(yǔ)言代碼實(shí)現(xiàn)猜數(shù)字游戲
大家好,本篇文章主要講的是C語(yǔ)言代碼實(shí)現(xiàn)猜數(shù)字游戲,感興趣的同學(xué)趕快來(lái)看一看了吧,對(duì)你有幫助的話記得收藏一下2022-01-01
C語(yǔ)言構(gòu)建動(dòng)態(tài)數(shù)組完整實(shí)例
這篇文章主要介紹了C語(yǔ)言構(gòu)建動(dòng)態(tài)數(shù)組完整實(shí)例,幫助讀者加深對(duì)C語(yǔ)言數(shù)組及指針的理解,需要的朋友可以參考下2014-07-07

