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 ...
随机推荐
- python关于for循环的几个函数
1.enumerate:返回2个值,1是当前的for循环的第几轮,2是循环得到的数值 enumerate works by supplying a corresponding index to eac ...
- Android 关于资源适配
一. 关于图片资源 图片宽高 不要固定大小,在小屏幕和大屏幕,不同分频率上 ,採用不同的图片,这个要美工切图的. 不同的分辨率,界面的长宽比不一致,须要不同规格的图片 在drawable-hdpi,d ...
- MongoDB获得短暂的
大约MongoDB该数据是现在比较少.和大多数英文网站.最上面的经笔者从官方网站翻译.请翻译或误解之处请作证.然后,我们将继续关注MongoDB,和翻译“Developer Zone”和“Admin ...
- Spring Bean的作用域(转)
Spring Bean的作用域 .singleton [单例] eg:<bean id="personService" class="com.yinger.ser ...
- Portal.MVC
Portal.MVC Portal.MVC 简介 项目是基于MVC4+EF,带有角色,权限,用户中心及账户相关(登录,注册,修改密码,找回密码等)等基本功能.参考的开源项目nopcommerce,这是 ...
- SICP的一些个人看法
网上搜书的时候,看到非常多人将这本书神话. 坦率地说,个人认为这本书过于学术化, 没什么实际project价值.一大堆题目也基本是高中数学竞赛题类似,浪费时间. 软件的核心技术是什么? 1> ...
- HDU 1557 权利指数 国家压缩 暴力
HDU 1557 权利指数 状态压缩 暴力 ACM 题目地址:HDU 1557 权利指数 题意: 中文题,不解释. 分析: 枚举全部集合,计算集合中的和,推断集合里面的团体是否为关键团队. 代码: ...
- Gradle 1.12 翻译——第十六章. 使用文件
有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...
- 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)
原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...
- 安装Microsoft .NET Framework 3.5 Service Pack 1回报1603错
server升级了一下系统补丁(360安装),所有发现.net无法打开网站,提示" 因为无法创建应用程序域,因此未能运行请求.错误: 0x80070002 系统找不到指定的文件. " ...