算法背景:
数据结构:
next数组 + 。。
算法原理:
预处理匹配串(匹配串与自己匹配)得到失配数组
没了。
得到失配数组NEXT的函数:
void getnext(char *p)//匹配串
{
int m=strlen(p);//匹配串的长度m
int k=-1,cur=0;//k是第二匹配串的下标,也是next的下标,cur是第一匹配串的下标
nxt[cur]=k;//初始化next的第0个元素为-1
while(cur<m)//第一匹配串的下标小于m
{
if(k==-1||p[cur]==p[k])//回到了第二匹配串的原点或者第一匹配串的第cur个字符和第二匹配串的第k个字符相同
nxt[++cur]=++k;//标记next-->之前的最长前后缀长度
else
k=nxt[k];//否则,向前回溯,在回溯的同时,也相当于把第二匹配串向后移动k-next[k]个单位
}
}
kmp主体:
void kmp(char *t,char *p)//文本串,匹配串
{
int n=strlen(t);//文本串长度
int m=strlen(p);//匹配串长度
int k=0,cur=0;//k是匹配串下标,cur是匹配串的下标
getnext(p);//找到失配数组
while(cur<n)//不用讲了
{
if(k==-1||t[cur]==p[k])//回到了匹配串的远点,或者两个串的下标对应相等
{
k++;//下标+1
cur++;//下标+1
}
else
k=nxt[k];//否则,匹配串往前回溯,和使匹配串往后移动k-next[k]个单位同理
if(k==m)//匹配完了
cout<<cur-m<<endl;//输出匹配成功的起始位置,自然是cur-m了
}
return;
}
CODE:
#include <iostream>
#include <cstring>
using namespace std;
char p[100],t[100];
int nxt[100];
void getnext(char *p)
{
int m=strlen(p);
int k=-1,cur=0;
nxt[cur]=k;
while(cur<m)
{
if(k==-1||p[cur]==p[k])
nxt[++cur]=++k;
else
k=nxt[k];
}
}
void kmp(char *t,char *p)
{
int n=strlen(t);
int m=strlen(p);
int k=0,cur=0;
getnext(p);
while(cur<n)
{
if(k==-1||t[cur]==p[k])
{
k++;
cur++;
}
else
k=nxt[k];
if(k==m)
{
cout<<cur-m<<endl;
k=-1;
cur-=1;
}
}
return;
}
int main()
{
cin>>t>>p;
kmp(t,p);
}
/*
abacabababab
abab
*/
算法解释:
1.整个算法的大致和求next数组的基本原理都在:
一、阮一峰--KMP
二、KMP-有意思
ORZ....
添加一个题集:
一个简单优化:
void getnext(char *p)
{
int m=strlen(p);
int k=-1,cur=0;
nxt[cur]=k;
while(cur<m)
{
if(k==-1||p[cur]==p[k])
{
if(p[++cur]!=p[++k])
nxt[cur]=k;
else
next[cur]=next[k];//在这里优化,我们将避免愚蠢的错误和明显的不匹配
}
else
k=nxt[k];
}
}
解释优化的原理:
网站:优化原理
关于kmp的返回值问题:
一、
返回所有(包括重叠)的模式串的起始位置:cur-m。//非常神奇,所以搞不明白
code:
if(k==m)
{
cout<<cur-m<<endl;
}
二、
返回所有(不包括重叠)的模式串的起始位置。
if(k==m)
{
cout<<cur-m<<endl;
k=-1;
cur--;
}
太简单了,不解释了
处理next数组当作周期串:
if(k==-1||p[cur]==p[k])
{
nxt[++cur]=++k;
zhouqi[cur]=cur-nxt[cur];//这里的周期就是字符串的最小循环节,求法:cur-nxt[cur]
}
在这里,还要提醒一点,next是求的前缀数组,而后缀数组是HASH做法,