一个:介绍KMP算法之前,首先解释一下BF算法

(1)BF算法(传统的匹配算法,是最简单的算法)

BF算法是一种常见的模式匹配算法,BF该算法的思想是目标字符串S模式串的第一个字符P的第一个字符,以匹配,如果相等,然后去比较S第二个字和P;若不相等。则比較S的第二个字符和P的第一个字符。依次比較下去。直到得出最后的匹配结果。

   

(2)举例说明:

    S:  ababcababa

    P:  ababa

  BF算法匹配的过程例如以下

           i=0                  i=1                       i=2                 i=3                   i=4

  第一趟:ababcababa         第二趟:ababcababa      第三趟:ababcababa    第四趟:ababcababa    第五趟:ababcababa

    ababa                         ababa                 ababa                 ababa                   ababa

    j=0                           j=1                   j=2                   j=3                     j=4(i和j回溯)



    i=1                           i=2                   i=3                   i=4                     i=3 

 第六趟:ababcababa         第七趟:ababcababa       第八趟:ababcababa     第九趟:ababcababa   第十趟:ababcababa

    ababa                   ababa                        ababa               ababa                   ababa

    j=0                     j=0                          j=1                 j=2(i和j回溯)           j=0



    i=4                     i=5                          i=6                 i=7                     i=8

第十一趟:ababcababa       第十二趟:ababcababa    第十三趟:ababcababa   第十四趟:ababcababa   第十五趟:ababcababa

       ababa                    ababa               ababa                    ababa                ababa

       j=0                       j=0                j=1                      j=2                  j=3

 

    i=9

第十六趟:ababcababa

 ababa

 j=4(匹配成功)

事实上在上面的匹配过程中,有非常多比較是多余的。在第五趟匹配失败的时候,在第六趟。i能够保持不变。j值为2。由于在前面匹配的过程中,对于串S,已知s0s1s2s3=p0p1p2p3。又由于p0!=p1!。所以第六趟的匹配是多余的。又由于p0==p2,p1==p3,所以第七趟和第八趟的匹配也是多余的。

在KMP算法中就省略了这些多余的匹配。

(3)BF代码:

int BFMatch(char* ori,char *des)
{
int i,j;
i = 0;
while(*(ori+i)!='\0')
{
j = 0;
while(*(ori+i)!='\0'&&*(des+j)!='\0'&&*(ori+i)==*(des+j))
{
i++;
j++;
}
if(*(des+j)=='\0')
return i-j;// 返回匹配成功后的src中的開始下标
i = i-j+1;// 回溯到,这次匹配的src中的開始位置的下一个位置
}
return -1;
}

二:KMP算法

(1)KMP算法之所以叫做KMP算法是由于这个算法是由三个人共同提出来的,就取三个人名字的首字母作为该算法的名字。

事实上KMP算法与BF算法的差别就在于KMP算法巧妙的

消除了指针i的回溯问题。仅仅需确定下次匹配j的位置就可以。使得问题的复杂度由O(mn)下降到O(m+n)。

在KMP算法中,为了确定在匹配不成功时,下次匹配时j的位置,引入了next[]数组,next[j]的值表示P[0...j-1]中最长后缀的长度等于同样字符序列的前缀。

对于next[]数组的定义例如以下:

 1) next[j] = -1  j = 0

 2) next[j] = max(k): 0<k<j   P[0...k-1]=P[j-k,j-1]

 3) next[j] = 0  其它



 如:

 P      a    b   a    b   a

 j      0    1   2    3   4

 next    -1   0   0    1   2

 

即next[j]=k>0时,表示P[0...k-1]=P[j-k,j-1]



因此KMP算法的思想就是:在匹配过程称,若发生不匹配的情况,假设next[j]>=0,则目标串的指针i不变。将模式串的指针j移动到next[j]的位置继续进行匹配;若next[j]=-1,则将i右移1位,并将j置0,继续进行比較。

(2)KMP算法通过next数组能够知道目标串中下一个字符是否有必要被检測,这个next数组就是用所谓的“前缀函数(一般数据结构书中的getNext函数)”来存储的。

这个函数可以反映出现失配情况时,系统应该跳过多少无用字符(也即模式串应该向右滑动多长距离)而进行下一次检測

一是这个前缀函数的求法。

二是在得到前缀函数之后。怎么运用这个函数所反映的有效信息避免不必要的检測。

  以下介绍《部分匹配表》是怎样产生的。

  首先,要了解两个概念:"前缀"和"后缀"。 "前缀"指除了最后一个字符以外,一个字符串的所有头部组合。"后缀"指除了第一个字符以外,一个字符串的所有尾部组合。

  "部分匹配值"就是"前缀"和"后缀"的最长的共同拥有元素的长度。以"ABCDABD"为例,

  - "A"的前缀和后缀都为空集,共同拥有元素的长度为0。

  - "AB"的前缀为[A],后缀为[B],共同拥有元素的长度为0;

  - "ABC"的前缀为[A, AB]。后缀为[BC, C]。共同拥有元素的长度0。

  - "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, 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。

(3) 代码例如以下:

#include <iostream>
#include <cstring> using namespace std;
const int MAX_SIZE = 64; void getNext(char *p,int next[])
{
int j,k;
next[0] = -1;
j = 0;
k = -1;
while(j<strlen(p))
{
if(k==-1 || p[j]==p[k])
{
j++;
k++;
next[j] = k;
}
else
k = next[k];
}
int i;
for(i=1;i<=j;i++)
cout << next[i] << ",";
} int KMPMatch(char *s,char *p)
{
int next[MAX_SIZE];
int i,j;
i = 0;
j = 0;
getNext(p,next);
while(s[i]!='\0')
{
if(j==-1 || s[i]==p[j])
{
i++;
j++;
}
else
{
j = next[j];// 消除指针回溯
}
if(p[j] == '\0')
return i-j;
}
return -1;
} int main()
{
char ori[MAX_SIZE],des[MAX_SIZE];
cout << "请输入两个字符串进行匹配:" << endl;
cin >> ori >> des;
//cout << "匹配结果:" << BFMatch(ori,des) << endl;
cout << "匹配结果:" << KMPMatch(ori,des) << endl;
return 0;
}

(4)总结:KMP是用来匹配test字符串是否是目标串的子串,相当于全然匹配。即測试字符串是否在目标字符串中出现过

三:最长公共子序列(LONGEST COMMEN SUBSEQUENCE)

(1)子序列:不要求连续的,子串是要求连续的。

(2)代码例如以下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream> using namespace std;
const int MAX_SIZE = 100;
int LCSLength(char *s1,char *s2,const int &len1,const int &len2,int lcs[][MAX_SIZE],int b[][MAX_SIZE])
{
int i,j;
for(i=0;i<=len1;i++)
lcs[0][i] = 0;
for(j=1;j<=len2;j++)
lcs[j][0] = 0;
for(i=1;i<=len1;i++)
{
for(j=1;j<=len2;j++)
{
if(s1[i-1] == s2[j-1])
{
lcs[i][j] = lcs[i-1][j-1] + 1;
b[i][j] = 0;
}
else if(lcs[i-1][j] >= lcs[i][j-1])
{
lcs[i][j] = lcs[i-1][j];
b[i][j] = 1;
}
else
{
lcs[i][j] = lcs[i][j-1];
b[i][j] = -1;
}
}
}
return lcs[len1][len2];
} void PrintLCS(char *s1,int b[][MAX_SIZE],int i,int j)
{
if(i==1 || j==0)
return;// 一定要与返回啊啊
if(b[i][j] == 0)
{
PrintLCS(s1,b,i-1,j-1);
cout << s1[i-1];
}
else if(b[i][j] == 1)
{
PrintLCS(s1,b,i-1,j);
}
else
{
PrintLCS(s1,b,i,j-1);
}
}
int main()
{
int len1,len2;
int lcs[MAX_SIZE][MAX_SIZE],b[MAX_SIZE][MAX_SIZE];
char s1[MAX_SIZE],s2[MAX_SIZE];
int ans;
while(cin >> s1 >> s2)
{
len1 = strlen(s1);
len2 = strlen(s2);
ans = LCSLength(s1,s2,len1,len2,lcs,b);
cout << ans << endl;
PrintLCS(s1,b,len1,len2);
cout << endl;
}
return 0;
}//

版权声明:本文博客原创文章。博客,未经同意,不得转载。

KMP该算法解释(最长公共子)的更多相关文章

  1. 使用后缀数组寻找最长公共子字符串JavaScript版

    后缀数组很久很久以前就出现了,具体的概念读者自行搜索,小菜仅略知一二,不便讨论. 本文通过寻找两个字符串的最长公共子字符串,演示了后缀数组的经典应用. 首先需要说明,小菜实现的这个后缀数组算法,并非标 ...

  2. 算法练习——最长公共子序列的问题(LCS)

    问题描述: 对于两个序列X和Y的公共子序列中,长度最长的那个,定义为X和Y的最长公共子序列.X  Y   各自字符串有顺序,但是不一定需要相邻. 最长公共子串(Longest Common Subst ...

  3. Java实现 蓝桥杯VIP 算法提高 最长公共子序列

    算法提高 最长公共子序列 时间限制:1.0s 内存限制:256.0MB 问题描述 给定两个字符串,寻找这两个字串之间的最长公共子序列. 输入格式 输入两行,分别包含一个字符串,仅含有小写字母. 输出格 ...

  4. uva 10066 The Twin Towers (最长公共子)

    uva 10066 The Twin Towers 标题效果:最长公共子. 解题思路:最长公共子. #include<stdio.h> #include<string.h> # ...

  5. ACM/ICPC 之 最长公共子序列计数及其回溯算法(51Nod-1006(最长公共子序列))

    这道题被51Nod定为基础题(这要求有点高啊),我感觉应该可以算作一级或者二级题目,主要原因不是动态规划的状态转移方程的问题,而是需要理解最后的回溯算法. 题目大意:找到两个字符串中最长的子序列,子序 ...

  6. 【算法】最长公共子序列(nlogn)

    转载注明出处:http://blog.csdn.net/wdq347/article/details/9001005 (修正了一些错误,并自己重写了代码) 最长公共子序列(LCS)最常见的算法是时间复 ...

  7. POJ 2774 后缀数组:查找最长公共子

    思考:其实很easy.就在两个串在一起.通过一个特殊字符,中间分隔,然后找到后缀数组的最长的公共前缀.然后在两个不同的串,最长是最长的公共子串. 注意的是:用第一个字符串来推断是不是在同一个字符中,刚 ...

  8. 算法实践--最长公共子序列(Longest Common Subsquence)

    什么是最长公共子序列 X=ACCG Y=CCAGCA 长度为1的公共子序列: {A} {C} {G} 长度为2的公共子序列:{AC} {CC} {CG} {AG} 长度为3的公共子序列:{ACG} 长 ...

  9. Java算法练习——最长公共前缀

    题目链接 题目描述 编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 "". 说明: 所有输入只包含小写字母 a-z . 示例 1 输入: [&qu ...

随机推荐

  1. python关于for循环的几个函数

    1.enumerate:返回2个值,1是当前的for循环的第几轮,2是循环得到的数值 enumerate works by supplying a corresponding index to eac ...

  2. Android 关于资源适配

    一. 关于图片资源 图片宽高 不要固定大小,在小屏幕和大屏幕,不同分频率上 ,採用不同的图片,这个要美工切图的. 不同的分辨率,界面的长宽比不一致,须要不同规格的图片 在drawable-hdpi,d ...

  3. MongoDB获得短暂的

    大约MongoDB该数据是现在比较少.和大多数英文网站.最上面的经笔者从官方网站翻译.请翻译或误解之处请作证.然后,我们将继续关注MongoDB,和翻译“Developer Zone”和“Admin ...

  4. Spring Bean的作用域(转)

    Spring Bean的作用域 .singleton  [单例] eg:<bean id="personService" class="com.yinger.ser ...

  5. Portal.MVC

    Portal.MVC Portal.MVC 简介 项目是基于MVC4+EF,带有角色,权限,用户中心及账户相关(登录,注册,修改密码,找回密码等)等基本功能.参考的开源项目nopcommerce,这是 ...

  6. SICP的一些个人看法

    网上搜书的时候,看到非常多人将这本书神话. 坦率地说,个人认为这本书过于学术化, 没什么实际project价值.一大堆题目也基本是高中数学竞赛题类似,浪费时间. 软件的核心技术是什么? 1>   ...

  7. HDU 1557 权利指数 国家压缩 暴力

    HDU 1557 权利指数 状态压缩 暴力 ACM 题目地址:HDU 1557 权利指数 题意:  中文题,不解释. 分析:  枚举全部集合,计算集合中的和,推断集合里面的团体是否为关键团队. 代码: ...

  8. Gradle 1.12 翻译——第十六章. 使用文件

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  9. 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)

    原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...

  10. 安装Microsoft .NET Framework 3.5 Service Pack 1回报1603错

    server升级了一下系统补丁(360安装),所有发现.net无法打开网站,提示" 因为无法创建应用程序域,因此未能运行请求.错误: 0x80070002 系统找不到指定的文件. " ...