KMP该算法解释(最长公共子)
一个:介绍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该算法解释(最长公共子)的更多相关文章
- 使用后缀数组寻找最长公共子字符串JavaScript版
后缀数组很久很久以前就出现了,具体的概念读者自行搜索,小菜仅略知一二,不便讨论. 本文通过寻找两个字符串的最长公共子字符串,演示了后缀数组的经典应用. 首先需要说明,小菜实现的这个后缀数组算法,并非标 ...
- 算法练习——最长公共子序列的问题(LCS)
问题描述: 对于两个序列X和Y的公共子序列中,长度最长的那个,定义为X和Y的最长公共子序列.X Y 各自字符串有顺序,但是不一定需要相邻. 最长公共子串(Longest Common Subst ...
- Java实现 蓝桥杯VIP 算法提高 最长公共子序列
算法提高 最长公共子序列 时间限制:1.0s 内存限制:256.0MB 问题描述 给定两个字符串,寻找这两个字串之间的最长公共子序列. 输入格式 输入两行,分别包含一个字符串,仅含有小写字母. 输出格 ...
- uva 10066 The Twin Towers (最长公共子)
uva 10066 The Twin Towers 标题效果:最长公共子. 解题思路:最长公共子. #include<stdio.h> #include<string.h> # ...
- ACM/ICPC 之 最长公共子序列计数及其回溯算法(51Nod-1006(最长公共子序列))
这道题被51Nod定为基础题(这要求有点高啊),我感觉应该可以算作一级或者二级题目,主要原因不是动态规划的状态转移方程的问题,而是需要理解最后的回溯算法. 题目大意:找到两个字符串中最长的子序列,子序 ...
- 【算法】最长公共子序列(nlogn)
转载注明出处:http://blog.csdn.net/wdq347/article/details/9001005 (修正了一些错误,并自己重写了代码) 最长公共子序列(LCS)最常见的算法是时间复 ...
- POJ 2774 后缀数组:查找最长公共子
思考:其实很easy.就在两个串在一起.通过一个特殊字符,中间分隔,然后找到后缀数组的最长的公共前缀.然后在两个不同的串,最长是最长的公共子串. 注意的是:用第一个字符串来推断是不是在同一个字符中,刚 ...
- 算法实践--最长公共子序列(Longest Common Subsquence)
什么是最长公共子序列 X=ACCG Y=CCAGCA 长度为1的公共子序列: {A} {C} {G} 长度为2的公共子序列:{AC} {CC} {CG} {AG} 长度为3的公共子序列:{ACG} 长 ...
- Java算法练习——最长公共前缀
题目链接 题目描述 编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 "". 说明: 所有输入只包含小写字母 a-z . 示例 1 输入: [&qu ...
随机推荐
- C++ 中的比較函数
在敲代码的时候,排序是一种和经常使用的算法.在排序中.比較又是当中最经常使用的操作.这里,我们来分析一下C++中的比較问题. 当中,基本数据类型int. float.string等已经提供了默认的比較 ...
- 【Java基础】异常的简单分类与处理
Java中所有的异常都继承自Throwable类,Throwable类的已知子类有Error和Exception. Error是指系统出现的错误,这种错误出现的时候,我们的程序无能为力,所以不需要进行 ...
- 标准I/O缓冲:全缓冲、行缓冲、无缓冲
说明:我仅仅对网络资源进行了整合,方便学习-.- 基于流的操作终于会调用read或者write函数进行I/O操作.为了使程序的执行效率最高,流对象一般会提供缓冲区,以降低调用系统I/O库函数的次数. ...
- hive load from hdfs出错
使用hive load从hdfs中load data的时候,hiveql如下: load data inpath 'hdfs://192.168.0.131:9000/hive/test.log' o ...
- ORACLE触发特定的解释
ORACLE PL/SQL编程八: 把触发器说透 本篇主要内容例如以下: 8.1 触发器类型 8.1.1 DML触发器 8.1.2 替代触发器 8.1.3 系统触发器 8.2 创建触发器 8.2.1 ...
- HTML介绍JS
首先,该脚本的链接插入HTML代码: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU2h1aVRpYW5OYWlMdW8=/font/5a6L5L2T/f ...
- 【Spring】Spring学习笔记-01-入门级实例
听说当前Spring框架很流行,我也准备好好学学Spring开发,并将学习的过程和大家分享,希望能对志同道合的同学有所帮助. 以下是我学习Spring的第一个样例. 1.Spring开发环境的搭建 我 ...
- offsetTop和scrollTop差异
最近写组件,这两个属性的结果搞的有点晕,我检查的文件及资料,这两个性质如下面总结: 他一直在offsetLeft.offsetTop,scrollLeft.scrollTop这些方法都是非常迷茫,花一 ...
- DSR on Openstack POC
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWFvbGlwaW5nNDU1bWxwNDU1/font/5a6L5L2T/fontsize/400/fil ...
- 【Web探索之旅】第二部分第一课:客户端语言
内容简介 1.第二部分第一课:客户端语言 2.第二部分第二课预告:服务器语言 第二部分:Web编程语言和工具 大家好.上一个部分我们学习了Web的一些基本概念: 什么是Web? Internet和We ...