博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
kmp
阅读量:5983 次
发布时间:2019-06-20

本文共 1725 字,大约阅读时间需要 5 分钟。

问题描述

给定一个主串S和模板串T,问T是否是S的子串?如S="aabcaad",T="caa",则T是S的子串, T=S[3,5],(我们这篇博文字符串下标一律从0开始)。

 

算法描述

最直接的方法是枚举S的每个位置作为开始位置,然后从i向后依次比较每个字母是否相等。设S的长度为n,T的长度为m,则这个方法的复杂度为O(nm)。

kmp方法解决这个问题跟上述方法有个区别,就是指向S的指针i不回溯。比如说,在某次比较中,我们发现S[i]!=T[j],那么i保持不变,T串向右滑动一段距离,用T的第k个字符与S的第i个字符比较(显然k小于j)。

那么满足什么样的关系时才能这样呢?由于S前面一段跟T[j]的前面相等,那么有S[i-j,i-1]=T[0,j-1],并且由于S[i]能直接与T[k]比较,那么S[i-k,i-1]=T[0,k-1],由于S[i-k,i-1]=T[j-k,j-1],所以T[0,k-1]=T[j-k,j-1]。并且没有比k更大的值p满足T[0,p-1]=T[j-p,j-1]。我们定义这个的值为next[j],它表示当T的第j个字符与S的某个字符匹配失败时,我们可以直接用T的第next[j]个字符与S的该字符比较。换句话说,我们还可以这样理解next[j],它表示最大的满足T[0,k-1]=T[j-k,j-1]的k值。

我们先不管这个next数组是怎么计算的,对于串S="abaabcac",它的next数组为:

有了这个next数组,我们寻找T是否是S子串的方法:

1 /** 2 返回T在S中第一次出现的位置 3 没有 则返回-1 4 */ 5 int kmpMatch(char s[],int lenS,char t[],int lenT,int next[]) 6 { 7     int i=0,j=0; 8     while(i
=lenT) return j-lenT;13 }14 return -1;15 }

下面,我们来看如何计算next数组。我们来递推计算next,首先next[0]=-1,并且我们计算出了0到j的next值,设k=next[j],那么有T[0,k-1]=T[j-k,j-1]。现在我们来计算next[j+1]的值。有两种情况。

第一种情况,T[j]==T[k],此时有T[0,k]=T[j-k,j],所以此时next[j+1]=k+1,也就是说next[j+1]=next[j]+1。
第二种情况,T[j]!=T[k],那么我们需要找一个最大的x,使得T[0,x-1]=T[j-x+1,j],那么next[j+1]=x。怎么找呢?由T[0,x-1]=T[j-x+1,j],可以得到 T[0,x-2]=T[j-x+1,j-1],设y=x-1,那么我们需要找到这样一个y,y满足T[0,y-1]=T[j-y,j-1]并且T[y]=T[j],那么y一定是next[j],next[next[j]],...,中的从前向后第一个满足T[y]=T[j]的。
下面是计算next的代码:

1 void getNext(char s[],int len,int next[]) 2 { 3     next[0]=-1; 4     int i=0,j=-1; 5     while(i

到此我们就说完了kmp的所有东西。但是,上面求出的next数组有一些缺陷。我们设T="aaaab",那么我们求出的next如下:

那么在匹配的时候,我们假设匹配到T[3]的时候失配了,那么按照next数组,接下来,我们将比较T[2]和S的那个字母,很明显又失败了(因为T[2],T[3]都是a),接着比较T[1],T[0],依次都失败了。这就是出现的问题。针对这个问题,我们对next数组进行以下改进:

1 void getNext_1(char s[],int len,int next[]) 2 { 3     next[0]=-1; 4     int i=0,j=-1; 5     while(i

对于T="aaaab"求出的next数组为:

转载地址:http://ffrox.baihongyu.com/

你可能感兴趣的文章
linux用户栈内核栈的设置---进程的创建: fork/execve【转】
查看>>
CookieContainer
查看>>
POJ 3264-Balanced Lineup(段树:单点更新,间隔查询)
查看>>
Hawk 数据抓取工具 使用说明(二)
查看>>
如何在Eclipse或者Myeclipse中使用tomcat(配置tomcat,发布web项目)?(图文详解)(很实用)...
查看>>
C#图片处理之: 另存为压缩质量可自己控制的JPEG
查看>>
[翻译]C#和COM的互操作
查看>>
PLSQL的 dynamic sql小例子
查看>>
[Spark][python]从 web log 中提取出 UserID 作为key 值,形成新的 RDD
查看>>
通用权限实现的核心设计思想
查看>>
一个自带简易数据集的模拟线性分类器matlab代码——实验训练
查看>>
30 个很棒的 PHP 开源 CMS 内容管理系统
查看>>
HDU ACM 1046 Gridland 找规律
查看>>
面试题36:数组中的逆序对
查看>>
Windows 8.1 新增控件之 TimePicker
查看>>
设计模式之美:Strategy(策略)
查看>>
使用 ALinq 实现 Linq to MySQL【转】
查看>>
C#实现平衡多路查找树(B树)
查看>>
重写Object.equals()方法和Object.hashCode()方法
查看>>
35.3. Node, Edge and Graph Attributes
查看>>