【POJ3461】KMP算法理解 for 初学者
来源:互联网 发布:linux查看磁盘空间 du 编辑:程序博客网 时间:2024/06/09 18:28
KMP算法
用途:求多字符串最长连续公共子串。或言用模式串b在字符串a中进行匹配。
例:a串:abbabcabababc
b串:ababc
最长连续公共子串即为a串最末的ababc
经典求法:
枚举a串的每个字符,以其为开始节点,枚举能匹配的最长长度,即比较a[i+j-1]是否等于b[j],相等则j++,不相等则跳出循环,记录f[i]=j-1得到以b串i为开始在a串中能匹配的最长长度,但这样的时间复杂度可以粗估计为O(lena*lenb);
这种枚举法简单易懂好实现,但是却会有很多冗余的操作。
比如以a串为abababc,b串为ababc,以a[1]为开始节点枚举到a[5]时,一看’a’!=’c’,失配!那这个时候如果按照经典算法就需要记录f[1]=4,然后以a[2]为开始节点重新枚举,可是这样有必要么?没有!我们完全可以把当前状态当作以a[3]为开始节点,而b串才刚刚匹配到第三个,这样是不是快了很多呢?这就是KMP算法的意义,而KMP又是如何将其实现的呢?
首先,由本应该由a[2]开始的枚举被缩进到了a[3],甚至我们都不需要从a[3]开始枚举,而是可以直接枚举a[5]与b[3]是否匹配,这个缩进过程就是kmp的精髓所在.而它为何可以直接从if(a[5]==b[3])开始呢?
仔细观察,b[1]==b[3],而b[2]==b[4]也就是说我们匹配到b[4]==a[4]时,实际上有a[4]==b[4]==b[2]&&a[3]==b[3]==b[1],这样一旦b[5]!=a[5],我们仍然有b[1]==a[3],b[2]==a[4],使得若b[3]==a[5],那么到a[5]这,依然可以有以其为末尾的公共连续子串,从另一方面讲,这相当于b串以a[3]为开头,在a中寻找公共连续子串,找到了3个的效果,只是快了很多.
但是这一切都是建立在我们知道b[1]==b[3],b[2]==b[4]的基础之上的,而这一步骤又是如何实现的呢?
我们可以通过建立一个数组pre,pre[i]表示模式串b在长度为i时的最长前缀长度,而这个前缀长度满足b的前pre[i]个和b从i倒数pre[i]个这么多个字符严格有序匹配.
比如ababxaba,pre数组为{0,0,1,2,0,1,2,3},为什么不是{1,2,3,4,5,6,7,8}呢?哦,我忘了说了,这个前缀还需要满足其为b串在长度为i时的真子串。
那么这个数组是怎么得到的呢?首先我们int一个“fix”表示目前前缀长度,若匹配成功,即b[fix+1]==b[i],则fix++没疑问吧?但是真正的精髓在于它如何处理失配情况:
while(fix&&b[fix+1]!b[i])fix=pre[fix];
而整个处理过程则为
for(fix=0,i=2;b[i]!=’\0’;i++){while(fix&&b[fix+1]!b[i])fix=pre[fix];if(b[fix+1]!b[i])fix++;pre[i]=fix;}
这个东西你手动模拟一下,再试试将while行语句和if行语句换个位置,感受一下代码的错误原因,理解就深刻多了。
比如defdekdefxdefdekdefdef,i枚举到最后一个d时,fix=9,此时失配,于是fix=pre[fix]=3,匹配上了,所以pre[i]=3+1=4;即此时能匹配4个,那这是为什么呢?我们分析若这个‘d’==该匹配到的‘x’,那么前缀的defdekdefx就==后缀的defdekdefd,但是失配了,而原来的最长前缀defdekdef中有最长前缀def==其后缀def==defdekdefxdefdekdef中最后的def,所以此时def依然既是模式串b的前缀,又是其后缀,所以若b[4]==’d’,那么依然是defdekdefxdefdekdefd中的最长前缀。
如此以来,我们就求得了模式串b的前缀数组。
前缀数组模板题为poj2752
Kmp裸题为poj3461
Poj3461题意,在目标串中求模式串出现最大次数,因为fix是目前匹配到的最长长度,所以只需要在fix==模式串长度时ans++即可,代码如下:
#include<cstdio>#include<cstring>#include<algorithm>#define N 1000using namespace std;int pre[N];char s[N];int main(){freopen("test.in","r",stdin);int n,i,len;scanf("%s",s+1);n=1;memset(pre,0,sizeof(pre));while(s[n]!='\0')n++;/*pre[1]=0,len=0;*/for(i=2;i<n;i++){while(len&&s[i]!=s[len+1])len=pre[len];if(s[i]==s[len+1])len++;pre[i]=len;}return 0;}/*两组利于理解的求自匹配前缀的模式串!efgdekxefgdefgenddefdekdefxdefdekdefdef*/PS:手调有助于深刻理解。
- 【POJ3461】KMP算法理解 for 初学者
- POJ3461 KMP算法
- KMP算法--poj3461
- KMP算法模板+POJ3461实现
- 再谈kmp算法 , 由poj3461
- HDU1686 POJ3461 Oulipo KMP算法
- POJ3461 Oulipo KMP算法应用
- kmp算法巩固 以poj3461为试验
- poj3461 KMP算法(字符串匹配)
- poj3461(KMP)
- POJ3461 KMP
- KMP+poj3461
- poj3461 KMP
- poj3461 KMP
- poj3461 KMP
- POJ3461 KMP
- poj3461 KMP
- POJ3461——Oulipo KMP算法的应用
- 智能家居发展之我见
- WAS优化
- Jmeter 教程
- 在Ubuntu下安装GCC编译器及GCC编译器的基本使用命令介绍
- Lession 5集合入门
- 【POJ3461】KMP算法理解 for 初学者
- ExtJS面向对象
- HTML (利用javascript ) 返回按钮
- Reverse Linked List II
- 记一次电脑重启后多个软件无法使用、崩溃问题
- C++引用
- spring设置编码过滤器
- 反射invoke()方法
- Apache hadoop nextGen mapreduce(yarn)