一个:介绍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. Javascript 设计模式 辛格尔顿

    转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/30490955 我一直很喜欢Js,,,今天写JsSingleton模式来实现,以及 ...

  2. windows phone 加速计(5)

    原文:windows phone 加速计(5) 在windows phone 中存在着加速计,我们可以利用加速计获得用户手机的状态,根据手机状态调整我们的程序,这样会更人性化:windows phon ...

  3. nginx subrequest演示示例程序

    只有简单subrequest应用演示示例. nginx.conf文件: #user nobody; worker_processes 1; #error_log logs/error.log; #er ...

  4. HDU 2828 DLX搜索

    Lamp Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

  5. Just like normal variables,

    Just like normal variables, pointers can be declared constant. There are two different ways that poi ...

  6. 设备Oracle当误差:环境不符合要求》》解决方法

    一旦安装Oracle当我常常会遇到这样的问题.也没太在意,改了一下client\stage\cvu文件夹cvu_prereq.xml档(添加支持目前的操作系统信息)为了克服,我没有做笔记,但后来有同学 ...

  7. JavaScript 内存

    JavaScript 中对内存的一些了解 在使用JavaScript进行开发的过程中,了解JavaScript内存机制有助于开发人员能够清晰的认识到自己写的代码在执行的过程中发生过什么,也能够提高项目 ...

  8. 初识google多语言通信框架gRPC系列(四)C++中使用gRPC

    我的这几篇文章都是使用gRPC的example,不是直接编译example,而是新建一个项目,从添加依赖,编译example代码,执行example.这样做可以为我们创建自己的项目提供借鉴.如果对gR ...

  9. SQL在declare声明变量

    在sql添加的声明变量. declare @local_variable data_type 你需要指定一个变量声明的类型, 能够使用set和select对变量进行赋值, 在sql语句中就能够使用@l ...

  10. 故障排查:是什么 导致了服务器端口telnet失败?(转)

    telnet命令的主要作用是与目标端口进行TCP连接(即完成TCP三次握手).当服务端启动后,但是telnet其监听的端口,却失败了.或者,当服务端运行了一段时间后,突然其监听的端口telnet不通了 ...