ImageVerifierCode 换一换
格式:DOCX , 页数:17 ,大小:62.65KB ,
资源ID:5383009      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/5383009.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(KMP算法详解.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

KMP算法详解.docx

1、KMP算法详解KMP算法详解(C+版)KMP算法是一种字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特莫里斯普拉特操作(简称KMP算法).KMP算法之所以难懂,很大一部分原因是很多实现的方法在一些细节的差异。然后去看另外的方法,就全都乱了!体现在几个方面:next数组,有的叫做“失配函数”,其实是一个东西;next数组中,有的是以下标为0开始的,有的是以1开始的;KMP主算法中,当发生失配时,取的next数组的值也不一样!就这样,各说各的,乱的很!所以,在阐述我的理解之前,我有必要说明一下,我是用next数组的,next数组是以下标0

2、开始的!还有,我不会在一些基础的概念上浪费太多,所以你在看这篇文章时必须要懂得一些基本的概念,例如“朴素字符串匹配”“前缀”,“后缀”等!假设在我们的匹配过程中出现了这一种情况:根据KMP算法,在该失配位会调用该位的next数组的值!在这里有必要来说一下next数组的作用!说的太繁琐怕你听不懂,让我用一句话来说明:返回失配位之前的最长公共前后缀!好,不管你懂不懂这句话,我下面的文字和图应该会让你懂这句话的意思以及作用的!首先,我们取之前已经匹配的部分(即蓝色的那部分!)我们在上面说到next数组的作用时,说到“最长公共前后缀”,体现到图中就是这个样子!接下来,就是最重要的了!没错,这个就是ne

3、xt数组的作用了:返回当前的最长公共前后缀长度,假设为len。因为数组是由0开始的,所以next数组让第len位与主串匹配就是拿最长前缀之后的第1位与失配位重新匹配,避免匹配串从头开始!如下图所示!(重新匹配刚才的失配位!)如果都说成这样你都不明白,那么你真的得重新理解什么是KMP算法了!接下来最重要的,也是KMP算法的核心所在,就是next数组的求解!不过,在这里我找到了一个全新的理解方法!如果你懂的上面我写的的,那么下面的内容你只需稍微思考一下就行了!跟刚才一样,我用一句话来阐述一下next数组的求解方法,其实也就是两个字:继承a、当前面字符的前一个字符的对称程度为0的时候,只要将当前字符

4、与子串第一个字符进行比较。这个很好理解啊,前面都是0,说明都不对称了,如果多加了一个字符,要对称的话最多是当前的和第一个对称。比如agcta这个里面t的是0,那么后面的a的对称程度只需要看它是不是等于第一个字符a了。b、按照这个推理,我们就可以总结一个规律,不仅前面是0呀,如果前面一个字符的next值是1,那么我们就把当前字符与子串第二个字符进行比较,因为前面的是1,说明前面的字符已经和第一个相等了,如果这个又与第二个相等了,说明对称程度就是2了。有两个字符对称了。比如上面agctag,倒数第二个a的next是1,说明它和第一个a对称了,接着我们就把最后一个g与第二个g比较,又相等,自然对称程

5、度就累加了,就是2了。c、按照上面的推理,如果一直相等,就一直累加,可以一直推啊,推到这里应该一点难度都没有吧,如果你觉得有难度说明我写的太失败了。当然不可能会那么顺利让我们一直对称下去,如果遇到下一个不相等了,那么说明不能继承前面的对称性了,这种情况只能说明没有那么多对称了,但是不能说明一点对称性都没有,所以遇到这种情况就要重新来考虑,这个也是难点所在。如果蓝色的部分相同,则当前next数组的值为上一个next的值加一,如果不相同,就是我们下面要说的!如果不相同,用一句话来说,就是:从前面来找子前后缀1、如果要存在对称性,那么对称程度肯定比前面这个的对称程度小,所以要找个更小的对称,这个不用

6、解释了吧,如果大那么就继承前面的对称性了。2、要找更小的对称,必然在对称内部还存在子对称,而且这个必须紧接着在子对称之后。如果看不懂,那么看一下图吧!下面介绍部分匹配表是如何产生的。首先,要了解两个概念:前缀和后缀。 前缀指除了最后一个字符以外,一个字符串的全部头部组合;后缀指除了第一个字符以外,一个字符串的全部尾部组合。部分匹配值就是前缀和后缀的最长的共有元素的长度。以ABCDABD为例,A的前缀和后缀都为空集,共有元素的长度为0;AB的前缀为A,后缀为B,共有元素的长度为0;ABC的前缀为A, AB,后缀为BC, C,共有元素的长度0;ABCD的前缀为A, AB, ABC,后缀为BCD,

7、CD, D,共有元素的长度为0;ABCDA的前缀为A, AB, ABC, ABCD,后缀为BCDA, CDA, DA, A,共有元素为A,长度为1;ABCDAB的前缀为A, AB, ABC, ABCD, ABCDA,后缀为BCDAB, CDAB, DAB, AB, B,共有元素为AB,长度为2;ABCDABD的前缀为A, AB, ABC, ABCD, ABCDA, ABCDAB,后缀为BCDABD, CDABD, DABD, ABD, BD, D,共有元素的长度为0。部分匹配的实质是,有时候,字符串头部和尾部会有重复。比如,ABCDAB之中有两个AB,那么它的部分匹配值就是2(AB的长度)。搜

8、索词移动的时候,第一个AB向后移动4位(字符串长度-部分匹配值),就可以来到第二个AB的位置。2.next数组的求解思路通过上文完全可以对kmp算法的原理有个清晰的了解,那么下一步就是编程实现了,其中最重要的就是如何根据待匹配的模版字符串求出对应每一位的最大相同前后缀的长度。我先给出我的代码:void makeNext(const char P,int next) int q,k;/q:模版字符串下标;k:最大前后缀长度 int m = strlen(P);/模版字符串长度 next0 = 0;/模版字符串的第一个字符的最大前后缀长度为0 for (q = 1,k = 0; q 0 & Pq

9、!= Pk)/递归的求出P0Pq的最大的相同的前后缀长度k k = nextk-1; /不理解没关系看下面的分析,这个while循环是整段代码的精髓所在,确实不好理解 (abcxabfsabcxabc) if (Pq = Pk)/如果相等,那么最大相同前后缀长度加1 k+; nextq = k; 现在我着重讲解一下while循环所做的工作:1. 已知前一步计算时最大相同的前后缀长度为k(k0),即P0Pk-1;2. 此时比较第k项Pk与Pq,如图1所示3. 如果PK等于Pq,那么很简单跳出while循环;4. 关键!关键有木有!关键如果不等呢?那么我们应该利用已经得到的next0nextk-1

10、来求P0Pk-1这个子串中最大相同前后缀,可能有同学要问了为什么要求P0Pk-1的最大相同前后缀呢?是啊!为什么呢?原因在于Pk已经和Pq失配了,而且Pq-k Pq-1又与P0 Pk-1相同,看来P0Pk-1这么长的子串是用不了了,那么我要找个同样也是P0打头、Pk-1结尾的子串即P0Pj-1(j=nextk-1),看看它的下一项Pj是否能和Pq匹配。如图2所示附代码:#include#includevoid makeNext(const char P,int next) int q,k; int m = strlen(P); next0 = 0; for (q = 1,k = 0; q 0

11、& Pq != Pk) k = nextk-1; if (Pq = Pk) k+; nextq = k; int kmp(const char T,const char P,int next) int n,m; int i,q; n = strlen(T); m = strlen(P); makeNext(P,next); for (i = 0,q = 0; i 0 & Pq != Ti) q = nextq-1; if (Pq = Ti) q+; if (q = m) printf(Pattern occurs with shift:%dn,(i-m+1); int main() int i

12、; int next20=0; char T100 = 0; char P100 = 0; scanf(%s%s,&T,&P); kmp(T,P,next); for (i = 0; i strlen(P); +i) printf(%d ,nexti); printf(n); return 0;poj2406(kmp next数组)大意:给出一个字符串 问它最多由多少相同的字串组成 Sample Inputabcdaaaaababab.Sample Output143分析:kmp中的next数组求最小循环节的应用#include #include #include using namespac

13、e std;const int maxn = 1000005;int nextmaxn;char smaxn;void get(const char s) int q,k; int m = strlen(s); next0 = 0; for (q = 1,k = 0; q 0 & sq != sk) k = nextk-1; if (sq = sk) k+; nextq = k; int main() while(gets(s) if(s0 =.) break; get(s); int ans = 1; int l = strlen(s); if(l % (l - nextl-1) = 0)

14、ans = l / (l - nextl-1); printf(%dn, ans); POJ 2752 Seek the Name, Seek the Fame(KMP)给你一个字符串s,从小到大输出s中既是前缀又是后缀的子串的长度。利用next数组的存储的性质,即可得出正确的答案。#include #include #include #include using namespace std;const int N = 400005;int NextN,ansN;char sN;void getnext() int j = 0,k = -1; int l = strlen(s); Next0

15、= -1; while(j = 1; i-) printf(%d ,ansi); printf(%dn,l);int main() while(scanf(%s,s) kmp(); return 0;poj 2185 题意:给出一个大矩阵,求最小覆盖矩阵,大矩阵可由这个小矩阵拼成。(就如同拼磁砖,允许最后有残缺)正确解法的参考链接:http:/poj.org/showmessage?message_id=153316在discuss里还看到有人说可以这么简化:求横向最小长度时每次比较整列求纵向最小长度时每次比较整行真的是太神了!http:/poj.org/showmessage?message

16、_id=168710一开始,我也是按照错误的解法来求得。也就是用KMP的next求出每行的最小循环子串长度,然后求这些长度的公倍数,作为宽(若大于col,则为col)。然后用KMP的next求出每列的最小循环子串长度,然后求出这些长度的公倍数,作为长(若大于row,则为row)。这种解法是过不了下面的样例的:Input2 8ABCDEFABABCDEABC2 8ABCDEFABAAAABAAAOutput1612对于第一个样例,可以这么做。但对于第二个样例,就不行了。因为AAAABAAA它的循环子串可以理解为AAAAB,也可以理解为AAAABA,AAAABAA,AAAABAAA而这里取AAAA

17、BA,正好与第一行的ABCDEF同样为6,所以答案为12。但是这样的解法也可以AC,说明POJ数据比较弱。#include #include #include #include #include #include using namespace std;const int maxn = 10000+10;const int maxm = 80;char matmaxnmaxm;char revmatmaxmmaxn;int r,c;int Pmaxn,Fmaxn;void getP() P1 = P0 = 0; for(int i = 1; i r; i+) int j = Pi; while

18、(j & strcmp(mati,matj) j = Pj; if(strcmp(mati,matj)=0) Pi+1 = j+1; else Pi+1 = 0; void getF() F1 = F0 = 0; for(int i = 1; i c; i+) int j = Fi; while(j & strcmp(revmati,revmatj) j = Fj; if(strcmp(revmati,revmatj)=0) Fi+1 = j+1; else Fi+1 = 0; void getRev() for(int i = 0; i c; i+) for(int j = 0; j r;

19、j+) revmatij = matji; void solve() int L = r-Pr,R = c - Fc; printf(%dn,L*R);int main() while(scanf(%d%d,&r,&c) for(int i = 0; i r; i+) scanf(%s,mati); getP(); getRev(); getF(); solve(); return 0;poj 3080 Blue Jeans大致题意:多组数据,每组给定m个字符串,求这些字符串最长公共子串。若子串长度小于3,则输出no significant commonalities,否则就输出这个最长公共子

20、串。若有多个最长公共子串(长度相等),则取其中字典序最小的那个。Input32GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATAGATACTAGATACTAGATACTAGATACTAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAAGATACCAGATACCAGATACC

21、AGATACCAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA3CATCATCATCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCACATCATCATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACATCATCATTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTOutputno significant commonalitiesAGATACCATCATCAT#include#includechar str2070;c

22、har tmp70;int p70;void getp(int m,char *b1) char b70; strcpy(b+1,b1); p1=0; int i,j=0; for(i=2;i0&bj+1!=bi) j=pj; if(bj+1=bi) j+=1; pi=j; bool kmp(char *a1,char *b1,int n,int m) char a70,b70; strcpy(a+1,a1);strcpy(b+1,b1); int i,j=0,cnt=0; for(i=1;i0&bj+1!=ai) j=pj; if(bj+1=ai) j+=1; if(j=m) return

23、true; return false;bool check(char *s,int tot) int i,j; for(i=2;i=tot;i+) int n=strlen(stri+1),m=strlen(s); if(!kmp(stri+1,s,n,m) return false; return true;char ans70;int main() int t,n,i,j; scanf(%d,&t); while(t-) scanf(%d,&n); for(i=1;i=n;i+) scanf(%s,stri+1); int len=strlen(str1+1); int L=0; for(i=1;i=len;i+) for(j=1;j=L) if(strcmp(tmp,ans)L) strcpy(ans,tmp); L=strlen(ans); memset(tmp,0,sizeof(tmp); if(L=3) puts(ans); else puts(no significant commonalities);

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1