KMP算法详解Word文档格式.docx
- 文档编号:18364846
- 上传时间:2022-12-15
- 格式:DOCX
- 页数:17
- 大小:62.65KB
KMP算法详解Word文档格式.docx
《KMP算法详解Word文档格式.docx》由会员分享,可在线阅读,更多相关《KMP算法详解Word文档格式.docx(17页珍藏版)》请在冰豆网上搜索。
数组的作用了
:
返回当前的最长公共前后缀长度,假设为
len
。
因为数组是由
开始的,所以
数组让第
len位与主串匹配就是拿最长前缀之后的第
位与失配位重新匹配,避免匹配串从头开始!
如下图所示!
(重新匹配刚才的失配位!
)
如果都说成这样你都不明白,那么你真的得重新理解什么是
算法了!
接下来最重要的,也是
算法的核心所在,就是
数组的求解!
不过,在这里我找到了一个全新的理解方法!
如果你懂的上面我写的的,那么下面的内容你只需稍微思考一下就行了!
跟刚才一样,我用一句话来阐述一下
数组的求解方法,其实也就是两个字:
继承
a
、当前面字符的前一个字符的对称程度为
的时候,只要将当前字符与子串第一个字符进行比较。
这个很好理解啊,前面都是
,说明都不对称了,如果多加了一个字符,要对称的话最多是当前的和第一个对称。
比如
agcta
这个里面
t
的是
,那么后面的
的对称程度只需要看它是不是等于第一个字符
了。
b
、按照这个推理,我们就可以总结一个规律,不仅前面是
呀,如果前面一个字符的
值是
,那么我们就把当前字符与子串第二个字符进行比较,因为前面的是
,说明前面的字符已经和第一个相等了,如果这个又与第二个相等了,说明对称程度就是
2
有两个字符对称了。
比如上面
agctag
,倒数第二个
的
是
,说明它和第一个
对称了,接着我们就把最后一个
g
与第二个
比较,又相等,自然对称程度就累加了,就是
c
、按照上面的推理,如果一直相等,就一直累加,可以一直推啊,推到这里应该一点难度都没有吧,如果你觉得有难度说明我写的太失败了。
当然不可能会那么顺利让我们一直对称下去,如果遇到下一个不相等了,那么说明不能继承前面的对称性了,这种情况只能说明没有那么多对称了,但是不能说明一点对称性都没有,所以遇到这种情况就要重新来考虑,这个也是难点所在。
如果蓝色的部分相同,则当前
数组的值为上一个
的值加一,如果不相同,就是我们下面要说的!
如果不相同,用一句话来说,就是:
从前面来找子前后缀
、如果要存在对称性,那么对称程度肯定比前面这个的对称程度小,所以要找个更小的对称,这个不用解释了吧,如果大那么就继承前面的对称性了。
、要找更小的对称,必然在对称内部还存在子对称,而且这个必须紧接着在子对称之后。
如果看不懂,那么看一下图吧!
下面介绍《部分匹配表》是如何产生的。
首先,要了解两个概念:
"
前缀"
和"
后缀"
"
指除了最后一个字符以外,一个字符串的全部头部组合;
指除了第一个字符以外,一个字符串的全部尾部组合。
部分匹配值"
就是"
的最长的共有元素的长度。
以"
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],共有元素为"
,长度为1;
ABCDAB"
的前缀为[A,AB,ABC,ABCD,ABCDA],后缀为[BCDAB,CDAB,DAB,AB,B],共有元素为"
,长度为2;
的前缀为[A,AB,ABC,ABCD,ABCDA,ABCDAB],后缀为[BCDABD,CDABD,DABD,ABD,BD,D],共有元素的长度为0。
部分匹配"
的实质是,有时候,字符串头部和尾部会有重复。
比如,"
之中有两个"
,那么它的"
就是2("
的长度)。
搜索词移动的时候,第一个"
向后移动4位(字符串长度-部分匹配值),就可以来到第二个"
的位置。
2.next数组的求解思路
通过上文完全可以对kmp算法的原理有个清晰的了解,那么下一步就是编程实现了,其中最重要的就是如何根据待匹配的模版字符串求出对应每一位的最大相同前后缀的长度。
我先给出我的代码:
voidmakeNext(constcharP[],intnext[])
{
intq,k;
//q:
模版字符串下标;
k:
最大前后缀长度
intm=strlen(P);
//模版字符串长度
next[0]=0;
//模版字符串的第一个字符的最大前后缀长度为0
for(q=1,k=0;
q<
m;
++q)//for循环,从第二个字符开始,依次计算每一个字符对应的next值
{
while(k>
0&
&
P[q]!
=P[k])//递归的求出P[0]·
·
P[q]的最大的相同的前后缀长度k
k=next[k-1];
//不理解没关系看下面的分析,这个while循环是整段代码的精髓所在,确实不好理解(abcxabfsabcxabc)
if(P[q]==P[k])//如果相等,那么最大相同前后缀长度加1
k++;
}
next[q]=k;
}
现在我着重讲解一下while循环所做的工作:
1. 已知前一步计算时最大相同的前后缀长度为k(k>
0),即P[0]·
P[k-1];
2. 此时比较第k项P[k]与P[q],如图1所示
3. 如果P[K]等于P[q],那么很简单跳出while循环;
4. 关键!
关键有木有!
关键如果不等呢?
?
那么我们应该利用已经得到的next[0]·
next[k-1]来求P[0]·
P[k-1]这个子串中最大相同前后缀,可能有同学要问了——为什么要求P[0]·
P[k-1]的最大相同前后缀呢?
是啊!
为什么呢?
原因在于P[k]已经和P[q]失配了,而且P[q-k]·
P[q-1]又与P[0]·
P[k-1]相同,看来P[0]·
P[k-1]这么长的子串是用不了了,那么我要找个同样也是P[0]打头、P[k-1]结尾的子串即P[0]·
P[j-1](j==next[k-1]),看看它的下一项P[j]是否能和P[q]匹配。
如图2所示
附代码:
#include<
cstdio>
cstring>
++q)
=P[k])
if(P[q]==P[k])
intkmp(constcharT[],constcharP[],intnext[])
intn,m;
inti,q;
n=strlen(T);
m=strlen(P);
makeNext(P,next);
for(i=0,q=0;
i<
n;
++i)
while(q>
=T[i])
q=next[q-1];
if(P[q]==T[i])
q++;
if(q==m)
printf("
Patternoccurswithshift:
%d\n"
(i-m+1));
}
intmain()
inti;
intnext[20]={0};
charT[100]={0};
charP[100]={0};
scanf("
%s%s"
&
T,&
P);
kmp(T,P,next);
for(i=0;
strlen(P);
%d"
next[i]);
\n"
);
return0;
poj2406(kmpnext数组)
大意:
给出一个字符串问它最多由多少相同的字串组成
SampleInput
abcd
aaaa
ababab
.
SampleOutput
1
4
3
分析:
kmp中的next数组求最小循环节的应用
#include<
iostream>
usingnamespacestd;
constintmaxn=1000005;
intnext[maxn];
chars[maxn];
voidget(constchars[])
intm=strlen(s);
s[q]!
=s[k])
if(s[q]==s[k])
intmain(){
while(gets(s)){
if(s[0]=='
.'
)break;
get(s);
intans=1;
intl=strlen(s);
if(l%(l-next[l-1])==0){
ans=l/(l-next[l-1]);
ans);
POJ2752SeektheName,SeektheFame(KMP)
给你一个字符串s,从小到大输出s中既是前缀又是后缀的子串的长度。
利用next数组的存储的性质,即可得出正确的答案。
string>
constintN=400005;
intNext[N],ans[N];
chars[N];
voidgetnext(){
intj=0,k=-1;
Next[0]=-1;
while(j<
l){
if(k==-1||s[j]==s[k]){
j++;
k++;
Next[j]=k;
else
k=Next[k];
voidkmp(){
getnext();
inti=l,sum=0;
while(i!
=0){
sum++;
ans[sum]=Next[i];
i=Next[i];
for(i=sum-1;
i>
=1;
i--)
ans[i]);
l);
intmain(){
while(~scanf("
%s"
s)){
kmp();
poj2185
题意:
给出一个大矩阵,求最小覆盖矩阵,大矩阵可由这个小矩阵拼成。
(就如同拼磁砖,允许最后有残缺)
正确解法的参考链接:
http:
//poj.org/showmessage?
message_id=153316
在discuss里还看到有人说可以这么简化:
求横向最小长度时每次比较整列
求纵向最小长度时每次比较整行
真的是太神了!
message_id=168710
一开始,我也是按照错误的解法来求得。
也就是用KMP的next求出每行的最小循环子串长度,然后求这些长度的公倍数,作为宽(若大于col,则为col)。
然后用KMP的next求出每列的最小循环子串长度,然后求出这些长度的公倍数,作为长(若大于row,则为row)。
这种解法是过不了下面的样例的:
Input
28
ABCDEFAB
ABCDEABC
AAAABAAA
Output
16
12
对于第一个样例,可以这么做。
但对于第二个样例,就不行了。
因为AAAABAAA它的循环子串可以理解为AAAAB,也可以理解为AAAABA,AAAABAA,AAAABAAA
而这里取AAAABA,正好与第一行的ABCDEF同样为6,所以答案为12。
但是这样的解法也可以AC,说明POJ数据比较弱。
vector>
queue>
constintmaxn=10000+10;
constintmaxm=80;
charmat[maxn][maxm];
charrevmat[maxm][maxn];
intr,c;
intP[maxn],F[maxn];
voidgetP(){
P[1]=P[0]=0;
for(inti=1;
r;
i++){
intj=P[i];
while(j&
strcmp(mat[i],mat[j]))j=P[j];
if(strcmp(mat[i],mat[j])==0)P[i+1]=j+1;
elseP[i+1]=0;
voidgetF(){
F[1]=F[0]=0;
c;
intj=F[i];
strcmp(revmat[i],revmat[j]))j=F[j];
if(strcmp(revmat[i],revmat[j])==0)F[i+1]=j+1;
elseF[i+1]=0;
voidgetRev(){
for(inti=0;
for(intj=0;
j<
j++){
revmat[i][j]=mat[j][i];
voidsolve(){
intL=r-P[r],R=c-F[c];
L*R);
%d%d"
r,&
c)){
i++)scanf("
mat[i]);
getP();
getRev();
getF();
solve();
poj3080BlueJeans
大致题意:
多组数据,每组给定m个字符串,求这些字符串最长公共子串。
若子串长度小于3,则输出nosignificantcommonalities,否则就输出这个最长公共子串。
若有多个最长公共子串(长度相等),则取其中字典序最小的那个。
2
GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
GATACTAGATACTAGATACTAGATACTAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA
GATACCAGATACCAGATACCAGATACCAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA
CATCATCATCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
ACATCATCATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AACATCATCATTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
nosignificantcommonalities
AGATAC
CATCATCAT
charstr[20][70];
chartmp[70];
intp[70];
voidgetp(intm,char*b1){
charb[70];
strcpy(b+1,b1);
p[1]=0;
inti,j=0;
for(i=2;
i<
=m;
i++){
while(j>
0&
b[j+1]!
=b[i])j=p[j];
if(b[j+1]==b[i])j+=1;
p[i]=j;
boolkmp(char*a1,char*b1,intn,intm)
chara[70],b[70];
strcpy(a+1,a1);
strcpy(b+1,b1);
inti,j=0,cnt=0;
for(i=1;
=n;
=a[i])j=p[j];
if(b[j+1]==a[i])j+=1;
if(j==m){
returntrue;
returnfalse;
boolcheck(char*s,inttot)
inti,j;
=tot;
i++)
intn=strlen(str[i]+1),m=strlen(s);
if(!
kmp(str[i]+1,s,n,m))
charans[70];
intt,n,i,j;
%d"
t);
while(t--)
n);
i++)scanf("
str[i]+1);
intlen=strlen(str[1]+1);
intL=0;
=len;
for(j=1;
j<
=len-i+1;
j++)
strncpy(tmp,str[1]+j,i);
memset(p,0,sizeof(p));
getp(i,tmp);
if(check(tmp,n))
{
if(i>
=L)
if(strcmp(tmp,ans)<
i==L)strcpy(ans,tmp);
L)strcpy(ans,tmp);
L=strlen(ans);
memset(tmp,0,sizeof(tmp));
if(L>
=3)puts(ans);
elseputs("
nosignificantcommonalities"
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- KMP 算法 详解