数据结构中的KMP算法怎样用C实现?

数据结构中的KMP算法怎样用C实现?,第1张

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>

#include <string.h>

#define MAX_LEN_OF_STR 30 // 字符串的最大长度

typedef struct String // 这里需要的字符串数组,存放字符串及其长度{

char str[MAX_LEN_OF_STR]// 字符数组

int length// 字符串的实际长度

}String, *PString

// 得到字符串的next数组

void GetNextArray(PString pstr, int next[])

{assert(NULL != pstr)<br/>assert(NULL != next)<br/>assert(pstr->length >0)// 第一个字符的next值是-1,因为C中的数组是从0开始的<br/>next[0] = -1<br/>for (int i = 0, j = -1i <pstr->length - 1)<br/>{// i是主串的游标,j是模式串的游标// 这里的主串和模式串都是同一个字符串<br/> if (-1 == j || // 如果模式串游标已经回退到第一个字符<br/>pstr->str[i] == pstr->str[j])// 如果匹配成功<br/> {// 两个游标都向前走一步<br/> ++i++j// 存放当前的next值为此时模式串的游标值<br/> next[i] = j<br/> }else // 匹配不成功j就回退到上一个next值

{j = next[j]}}}

// KMP字符串模式匹配算法

// 输入: S是主串,T是模式串,pos是S中的起始位置

// 输出: 如果匹配成功返回起始位置,否则返回-1

int KMP(PString S, PString T, int pos)

{assert(NULL != S)<br/>assert(NULL != T)<br/>assert(pos >= 0)<br/>assert(pos <S->length)<br/>if (S->length <T->length)<br/>return -1<br/>printf("主串\t = %s\n", S->str)<br/>printf("模式串\t = %s\n", T->str)<br/>int *next = (int *)malloc(T->length * sizeof(int))// 得到模式串的next数组<br/>GetNextArray(T, next)<br/迹毕孙>int i, j<br/>for (i = pos, j = 0i <S->length &&j <T->length){// i是主串游标,j是模式串游标<br/> if (-1 == j || // 模式串游标已经回退到第一个位置数和<br/> S->str[i] == T->str[j]) // 当前字符匹配成功<br/> {// 满足以上两种情况姿链时两个游标都要向前进一步<br/> ++i++j}

else // 匹配不成功,模式串游标回退到当前字符的next值

{j = next[j]}}

free(next)

if (j >= T->length)

{// 匹配成功

return i - T->length}

else{// 匹配不成功

return -1}}

朴素算法

先看看最“朴素”的算法: ///find a template in a string. #include<string.h>#include<stdio.h>int Index(char *S, char *T, int pos) { int k=pos, j=0while(k <strlen(S) &&j<strlen(T))//未超出字符串的长度 { if (S[k] == T[j]) { ++k++j} //如果相同,则继续向后比较 else {k = k-j+1j =0} //如果不同,就回溯,重新查找 } if (j == strlen(T)) return k-strlen(T)else return 0}

编辑本段KMP算法

一种由Knuth(D.E.Knuth)、Morris(J.H.Morris)和Pratt(V.R.Pratt)三人设计的线性时间字符串匹配算法。这个算法不用计算变迁函数δ,匹配时间为Θ(n),只用到辅助函数π[1,m],它是在Θ(m)时间内,根据模式预先计算出来的。数组π使得我们可以按需要,“现场”有效的计算(在平摊意义上来说)变迁函数δ。粗略地说,对任意状态q=0,1,…,m和任意字符a∈Σ,π[q]的值包含了与a无关但在计算δ(q,a)时需要的信息。由于数组π只有m个元素,而δ有Θ(m∣Σ∣)个值,所以通过预猛游先计算π而不是δ,使得时间减少了一个Σ因子。[1] KMP算法是通过分析子串,预先计算每个位置发生不匹配的时候,所需GOTO的下一个比较位置,整理出来一个next数组,然后在上面的算法中使用。

编辑本段KMP算法的讲解

当我们分析一个子串时,例如:abcabcddes. 需要分析一下,每个禅游字符x前面最多有多少个连续的字符和字符串从初始位置开始的字符匹配。然后+1就行了(别忘了,我们的字符串都是从索引1开始的)当然,不要相同位置自己匹配,默认第一个字符的匹配数是0。

编辑本段定义

设字符串为 x1x2x3...xn ,其中x1,x2,x3,... xi,... xn均是字符,设ai为字符xi对应的整数。则a=m,当且贺知销仅当满足如下条件:字符串x1x2...xm equals 字符串x(i-m+1)...xi-1 xi 并且x1x2...xm x(m+1) unequals x(i-m) x(i-m+1)...xi-1 xi。

编辑本段举例

abcabcddes 0111234111 |----------------------默认是0 --| | |-----------------不能自己在相同位置进行字符匹配,所以这里认为没有匹配字符串,所以0+1 =1,继续从1开始匹配 ------| | |-----------前面的字符和开始位置的字符相同,所以是2,3,4 -----------| | | |-------不匹配只能取1。 希望能明白的是,如果开始字符是 Ch1的话,那么我们就是要在串中第2个Ch1后面的位置开始自己和自己匹配,计算最大的吻合度。 程序写出来就是: void GetNext(char* T, int *next) { int k=1,j=0next[1]=0while( k〈 T[0] ){ if (j ==0 || T[k] == T[j]) { ++k++jnext[k] = j} else j= next[j]} } 但是这个不是最优的,因为他没有考虑aaaaaaaaaaaaaaaaaaab的情况,这样前面会出现大量的1,这样的算法复杂度已经和最初的朴素算法没有区别了。所以稍微改动一下: void GetNextEx(char *T, char *next) { int k=1,j=0next[1] = 0while(k <T[0]) { if (j == 0 || T[k] == T[j]) { ++k++jif (T[k] == T[j]) next[k] = next[j]else next[k] = j} else j = next[j]} } 现在我们已经可以得到这个next字符串的值了,接下来就是KMP算法的本体了: 相当简单: int KMP(char* S, char* T, int pos) { int k=pos, j=1while (k){ if (S[k] == T[j]){ ++k++j} else j = next[j]} if (j>T[0]) return k-T[0]else return 0} 和朴素算法相比,只是修改一句话而已,但是算法复杂度从O(m*n) 变成了:O(m)

编辑本段KMP算法的伪代码

KMP-MATCHER(T,P) 1 n ← length[T] 2 m ←length[P] 3 π ← COMPUTE-PREFIX-FUNCTION(P) 4 q ← 0 △Number of characters matched. 5 for i ← 1 to n △Scan the text from left to right. 6 do while q>0 and P[q+1]≠T[i] 7 do q ← π[q] △Next character does not match. 8 if P[q+1]=T[i] 9 then q ← q+1 △Next character matches. 10 if q=m △Is all of P matched? 11 then print “Pattern occurs with shift” i-m 12 q ← π[q] △Look for the next match. COMPUTE-PERFIX-FUNCTION(P) 1 m ← length[P] 2 π[1] ← 0 3 k ← 0 4 for q ← 2 to m 5 do while k>0 and P[k+1]≠P[q] 6 do k ← π[k] 7 if P[k+1]=P[q] 8 then k ← k+1 9 π[q] ← k 10 return π[1]

编辑本段KMP算法的c++实现

//c++实现的KMP算法,所有涉及字符串,其初始下标从0开始(上述算法均是从1开始) //example: char s[100],t[100]cin>>s>>tKMP(s,t)//获取待查询模式的next数组 int* get_next(char* T, int* next){ int i = 0, j = -1int length = strlen(T)int *temp = next*next = -1while(i<length){ if(j==-1 || *(T+i)==*(T+j)){ i++j++//优化后的get_next方法,可以防止出现形如"aaaaab"这种模式的计算退化 if(*(T+i)!=*(T+j)) *(next+i)=jelse *(next+i)=*(next+j)} else j=*(next+j)} return temp} //KMP算法 int KMP(char *S, char *T){ int S_Length = strlen(S)int T_Length = strlen(T)//若模式长度大于字符串,则直接返回查询失败 if( S_Length <T_Length) return 0int i = 0, j = 0int* next = new int[T_Length]get_next(T, next)while(i <S_Length &&j <T_Length){ if(j == -1 || *(S+i) == *(T+j)){ i++j++} else j=*(next+j)} if(j>=T_Length) return i-T_Lengthreturn 0} 在此提供一个更简明的适用于字符串的kmp实现: #include<iostream>#include<string.h>int next[100]void getnext(char b[]) { int i=1,j=0//i是每个位子,j是回退的位子 next[1]=0while(i<=strlen(b)) { if(j==0||b[i-1]==b[j-1]) { i++j++next[i]=j} else j=next[j]//用上一个的 回退关系 } } int kmp(char a[],char b[]) { int i=1,j=1//i是主串中的位子 ,j匹配串的位子 while(i<=strlen(a)&&j<=strlen(b)) { if(j==0||a[i-1]==b[j-1]) { i++j++} else j=next[j]} if(j>strlen(b)) return i-strlen(b)else return 0} int main() { char a[40],b[40]printf("要匹配的主串:\n")scanf("%s",a)printf("要匹配的子串:\n")scanf("%s",b)getnext(b)printf("输出next值:\n")for(int i=1i<=strlen(b)i++) printf("%d ",next[i])printf("\n")printf("%d",kmp(a,b))system("pause")main()return 0}

编辑本段串的最大匹配算法

摘要:

给定两个串S和T,长分别m和n,本文给出了一个找出二串间最大匹配的算法。该算法可用于比较两个串S和T的相似程度,它与串的模式匹配有别。

关键词:

模式匹配 串的最大匹配 算法 Algorithm on Maximal Matching of Strings Lin YuCai Xiang YongHong Zhang ChunXia Zhang JianJun (Computer Science Department of Yunnan Normal University Kunming 650092) ABSTRACT Given Two Strings S of length m and T of length n,the paper presents an algorithm which finds the maximal matching of them. The algorithm can be used to compare the similarility of the two strings S and T, it is different with the strings' pattren matching. KEY WORDS Pattern Matching Maximal Matching of Strings Algorithm

编辑本段问题的提出

字符串的模式匹配主要用于文本处理,例如文本编辑。文本数据的存储(文本压缩)和数据检索系统。所谓字符串的模式匹配[2],就是给定两个字符串S和T,长度分别为m和n,找出T中出现的一个或多个或所有的S,在这方面已经取得了不少进展[3][4][5][6][7][8][9][10][11]。本文从文本处理的另一个角度出发,找出两个串的最大匹配,比较其相似程度[1]。它主要应用于文本比较,特别是在计算机辅助教学中。显然前者要找S的完全匹配,而后者并无此要求。例如,若S=ABCD,T=EFABCDX,那么模式匹配的结果就是找出了T中的一个ABCD,而我们算法的结果就是S能与T的ABCD完全匹配,但是T中还有3个字符是比S多出来的,也就是说在S中有100%的字符与T中的匹配,而在T中有57%的字符与S中的匹配。若S= ABCDFE,T=AFXBECDY。则在模式匹配中S与T无匹配项,但在我们的算法中就能发现T中存在A,B,C,D,但D后不存在E,F。而且S中也存在A,B,C,D,且具有顺序性。这样就能公正地评价S,T的区别。得知其相似程度。 文章的组织如下:首先介绍基本定义和问题的描述;第三节是算法设计;最后是本文总结。

编辑本段问题的描述

设∑为任意有限集,其元称为字符,w:∑→N为∑到N的函数,称为∑的权函数(注:本文仅讨论权值恒为1的情况)。∑*为∑上的有限字符串集合,那么对任意S,T∈∑*,设S=a1a2…am,T=b1b2…bn,m>0,n>0。记<m>={1,2, …,m},<n>={1,2, …,n},则称{(i,j)∣i∈<m>,j∈<n>,ai=bj}为S与T的匹配关系集,记作M(S,T),称M为S与T的一个(容许)匹配,若对任意(i,j), ( i',j' )∈,① i<i',当且仅当j<j',② i= i'当且仅当j= j'。S与T的匹配中满足 最大者,称为S与T的最大匹配。若C(i,j)为N上的m×n矩阵,且满足: 则称矩阵C为串S与T的匹配关系阵。 于是求串S与T的最大匹配,等价于求C中的一个最大独立点集M,它满足,若ci,j,ci',j'∈M,则i<i' 当且仅当j<j',i=i'当且仅当j=j'。我们称这样的最大独立点集为C的最大C-独立点集。 例:设∑为所有字母的集合,对任意x∈∑,w(x)≡1,设S与T分别为:S=“BOOKNEWS”,T=“NEWBOOKS”。则我们可以得到S与T两个匹配: 这里=5; 这里 =4。 显然为串S与T的最大匹配。 S与T的匹配关系阵C可表示如下: 其中带圈的部分为一最大C-独立点集。

编辑本段算法设计

我们仅就权值为一的情况进行讨论。 设S和T为任意给定串,C为的S与T匹配关系阵,那么由2的讨论知,求S与T的最大匹配问题,等价于求C的最大C-独立点集问题。因而,为了解决我们的问题,只要给出求C的最大C-独立点集的算法就可以了。 显然,为了求出C的最大C-独立点集,我们可以采用这样的方法:搜索C的所有C-独立点集,并找出它的最大者。这种方法是可行的,但并不是非常有效的。这会使问题变得很繁,复杂度很大。因此,我们先对问题进行分析。 在下面的讨论中,我们把C的任一C-独立点集={ai1,j1,…,ais,js},记作=ai1,j1…ais,js,i1 <…<is。于是可看作阵C中以1为节点的一条路,满足:对路中的任意两节点,均有某一节点位于另一节点的右下方。称这种路为右下行路。 于是求C-独立点集等价于求阵C的右下行路。这种求右下行路的搜索可以逐行往下进行。 命题1. 若 =αai,jβ和ψ=α'ai,jσ为C的两个C-独立点集,且α为α'的加细,则存在C-独立点集'=αai,jδ,满足≥。 命题2. 若 =αai,jβ和ψ=α'ai+k,jσ为C的两个C-独立点集,且≥,则存在C-独立点集'=αai,jδ,满足≥。 命题3. 若 =αai,jβ和ψ=α'ai,j+kσ为C的两个C-独立点集,且≥,则存在C-独立点集'=αai,jδ,满足≥。 由命题1知,在搜索右下行路的过程中,如果已获得了某一C-独立点集的某一初始截段αai,j和另一C-独立点集ψ的某一初始截段α'ai,j,且有≤,则我们可以停止对ψ的进一步搜索。 由命题2知,在搜索右下行路的过程中,在某一列j存在某两个C-独立点集的某初始截段=ai1,j1…ais,j和ψ=al1,m1…alt,j,如果≥,但lt>is,则我们可以停止对ψ的进一步搜索。 由命题3知,在搜索右下行路的过程中,在某一行i存在某两个C-独立点集的某初始截段=ai1,j1…ai,js和ψ=ai1,m1…ai,mt,如果≥,但mt>js,则我们可以停止对ψ的进一步搜索。 由此可见,并不要求搜索所有C的最大C-独立点集,而可以采用比这简单得多的方法进行计算。那么按照我们上面的三个命题,来看如下实例: 首先我们得到=B(在上的节点用①表示),我们向右下方找路,可以发现,在第4列有两个1,根据命题2,我们选择上面的一个1,也就是说选择第1行的那个1,而不要第2行的那个1。同时我们也发现在第1行也有两个1,由命题3知,我们选择左边的那个1,即第4列的那个1。此时=BO。但是当我们的算法运行到第4行时,=BOOK,由于K在第3行第6列,而本行的1在第1列,在路最后一个节点K的左边,那么我们必须新建一条路ψ,因为我们并不能确定是否以后就有≥,当算法运行到第6行时,=BOOK,ψ=NEW,=4,=3,我们将S链到路上,此时我们得到最长右下行路=BOOKS,=5。这样我们就可以计算出这两个字符串的匹配程度。 在我们的算法设计过程中,用到了两个技巧。技巧之一,矩阵C不用存储,是动态建立的,节省了空间。技巧之二,本算法并不要求所有的S与T中所有的元素都相互进行比较,也并不存储所有的右下行路,节省了时间和空间。由矩阵中1的出现情况可见,本算法所需的空间和时间都远小于O(mn)

编辑本段结束语

本文给出了一个与模式匹配不同的,具有若干应用的,串的最大匹配算法,该算法已经在机器上实现,达到了预期的效果。本文仅讨论权值恒为1的情况,对于权值任意的情形不难由此得到推广。

编辑本段C语言代码(C Code)

#include<stdio.h>#include<string.h>void getnext(int next[],char s[],int l) { int i=1,j=0next[1]=0while(i<l) { if(j==0 || s[i]==s[j]) { i++j++next[i]=j} else j=next[j]} } int KMP(char s1[],char s2[],int l1,int l2,int next[]) { int i,ji=j=1while(i<=l1 &&j<=l2) { if(j==0||s1[i]==s2[j]) { i++j++} else j=next[j]} if(j>l2) return(i-l2)return 0} int main() { int next[10001],anschar s1[10001],s2[10001],l1,l2scanf("%s",s1+1)scanf("%s",s2+1)l1=strlen(s1+1)l2=strlen(s2+1)getnext(next,s2,l2)ans=KMP(s1,s2,l1,l2,next)if(ans!=0) printf("%d\n",ans)else printf("No!\n")system("pause")return 0}

编辑本段KMP算法的pascal实现

var next:array [1 ..1000001] of longints,t:ansistringprocedure get_next(t:ansistring)var j,k:integerbegin j:=1k:=0while j<length(t) do begin if (k=0) or (t[j]=t[k]) then begin inc(j)inc(k)next[j]:=kend else k:=next[k]endendfunction index(s:ansistringt:ansistring):longintvar i,j:longintbegin get_next(t)index:=0i:=1j:=1while (i<=length(s))and(j<=length(t)) do begin if (j=0)or(s[i]=t[j]) then begin inc(i)inc(j)end else j:=next[j]if j>length(t) then index:=i-length(t)endendbegin readln(s)readln(t)writeln(index(s,t)) end.

编辑本段KMP播放器

K-multimedia player的缩写

来自韩国的影音全能播放器,与Mplayer一样从linux平台移植而来的Kmplayer(简称KMP)几乎可以播放您系统上所有的影音文件。通过各种插件扩展KMP可以支持层出不穷的新格式。强大的插件功能,直接从Winamp继承的插件功能,能够直接使用winamp的音频 ,输入,视觉效果插件,而通过独有的扩展能力,只要你喜欢,可以选择使用不同解码器对各种格式进行解码。 KMPlayer The Professional Media Player! 它支持 Winamp 2/5 的输入、常规、DSP、视觉效果、媒体库插件。无须注册表支持直接调用 Directshow 滤镜!FFdshow 的视觉特效系统~超强的 GUI 界面~安装电视卡后可以直接代替原软件直接收看电视~支持播放 DVD/VCD 以及绝大多数电脑的媒体文件(AVI 支持 Xvid/DivX/3vid/H264 OGG/OGM/MKV 容器/AC3/DTS 解码~Monkey Audio 解码~)强烈推荐!此播放器除了会将自己的配置信息写入注册表外绝对绿色~ KMplayer内置目前常见的所有解码器,包括real,QT等。 另外KMplayer安装版也是目前很少见的检查流氓软件的安装方式,如果一旦有恶意的汉化小组汉化并捆绑了流氓软件。该安装程序自动会识别,并作出提示,建议用户不要安装,虽然不是特别准确,但KMplayer的无广告及第三方插件的特点使其深受好评。 目前韩国官方已经在Kmplayer里自带了中文字库,只要用户是中文系统,软件就会自动识别,十分方便。 KMP版本: KMPlayer3.0.0.1439

KMP算法查亮拍纳找串S中含串P的个数count #include <iostream> #include <stdlib.h> #include <vector> using namespace std inline void NEXT(const string&T,vector<int>&next) { //按模式串生成vector,next(T.size()) next[0]=-1 for(int i=1i<T.size()i++ ){ int j=next[i-1] while(T[i]!=T[j+1]&&j>=0 ) j=next[j] /贺兆/递推计算 if(T[i]==T[j+1])next[i]=j+1 else next[i]=0// } } inline string::size_type COUNT_KMP(const string&S, const string&T) { //利用模式串T的next函数求T在主串S中的个数count的KMP算法 //其中T非空, vector<int>next(T.size()) NEXT(T,next) string::size_type index,count=0 for(index=0index<S.size()++index){ int pos=0 string::size_type iter=index while(pos<T.size() &&iter<敬没S.size()){ if(S[iter]==T[pos]){ ++iter++pos } else{ if(pos==0)++iter else pos=next[pos-1]+1 } }//while end if(pos==T.size()&&(iter-index)==T.size())++count } //for end return count } int main(int argc, char *argv[]) { string S="abaabcacabaabcacabaabcacabaabcacabaabcac" string T="ab" string::size_type count=COUNT_KMP(S,T) cout<<count<<endl system("PAUSE") return 0 } 补上个Pascal的KMP算法源码 PROGRAM Impl_KMP USES CRT CONST MAX_STRLEN = 255 VAR next : array [ 1 .. MAX_STRLEN ] of integer str_s, str_t : string int_i : integer Procedure get_nexst( t : string ) Var j, k : integer Begin j := 1k := 0 while j <Length(t) do begin if ( k = 0 ) or ( t[j] = t[k] ) then begin j := j + 1k := k + 1 next[j] := k end else k := next[k] end End Function index( s : stringt : string ) : integer Var i, j : integer Begin get_next(t) index := 0 i := 1j := 1 while ( i <= Length(s) ) and ( j <= Length(t) ) do begin if ( j = 0 ) or ( s[i]= t[j] ) then begin i := i + 1j := j + 1 end else j := next[j] if j >Length(t) then index := i - Length(t) end End BEGIN ClrScr{清屏,可不要} Write(‘s = ’) Readln(str_s) Write(‘t = ’) Readln(str_t) int_i := index( str_s, str_t ) if int_i <>0 then begin Writeln( 'Found' , str_t,' in ', str_s, 'at ', int_i,' .' ) end else Writeln( 'Cannot find ', str_t,' in' , str_s, '. ') END.


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12402307.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-25
下一篇 2023-05-25

发表评论

登录后才能评论

评论列表(0条)

保存