您的位置:首页 > 其它

【hiho一下】第三周 KMP算法

2014-07-27 10:47 471 查看
题目1:KMP算法

题目原文:http://hihocoder.com/contest/hiho3/problem/1

【题目解读】

就是KMP算法的基本使用之一。添加了计数功能。

KMP算法的核心思想,就是通过减少匹配失败时模式串的回溯位数,来减少匹配次数。此时回溯的时候,使用 next 数组。

求解 next 数组见:http://www.cnblogs.com/dolphin0520/archive/2011/08/24/2151846.html,需要注意的是,这里用到了数学归纳法。即:

假设:根据定义next[0]=-1。然后,假设next[j]=k, 即P[0...k-1]==P[j-k,j-1]

则有:1)若P[j]==P[k],则有P[0..k]==P[j-k,j],很显然,next[j+1]=next[j]+1=k+1;     

    2)若P[j]!=P[k],则可以把其看做模式匹配的问题,即匹配失败的时候,k值如何移动,显然k=next[k]

next 数组的求解,可以视为相错一位的模式串,其中一个作为原始串,另外一个作为模式串,进行匹配,失败则回溯

【编写细节】

(1)想要理解KMP算法的主体比较部分,建议先实现 BF算法,就是最普通的暴力匹配。如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

char ori[1000005];
char par[10005];
int next[10005];

int main()
{
int t;
scanf("%d", &t);
long olen;
int plen;
int ans;
while(t--)
{
ans = 0;
memset(ori, 0, sizeof(ori));
memset(par, 0, sizeof(par));
memset(next, 0, sizeof(next));

scanf("%s", par);
scanf("%s", ori);

olen = strlen(ori);
plen = strlen(par);

for(long i=0; i<olen; i++)
{
long j = i;
int k = 0;

while(ori[j] == par[k])
{
j++;
k++;
if(k >= plen) break;
}

if(k>=plen) ans++;
}
printf("%d\n", ans);
}
return 0;
}


对照这个再去写KMP的主题匹配,就能理解 for 里面的第一个while为了找到原始串与模式串的第一个匹配点(不一定是模式串的第一个字符);

(2)KMP里面如果不匹配就回溯(绝不用--),一律使用k = next[k],不会使用到 k--;只要回溯了第一次,之后不匹配即可不断回溯直至找到匹配或者直接到头;

(3)一定要注意!KMP算法需要单独考虑模式串与原始串长度均为1时的情况!次数 next 数组只有一个值-1;

(4)此题是KMP匹配的计数功能,所以需要原始串的一位一位字符扫描(for循环),以防遗漏ABABA中有两个ABA的情况;

(5)其余细节写在代码注释。。。很难搞,当模板记下来吧。

【AC代码】

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

char ori[1000005];
char par[10005];
int next[10005];
int t, plen;
long olen, ans;

void get_next(int len)
{
next[0] = -1;
int j = 0;
int k = -1;
while(j < len)
{
// 一直找到可以匹配的开始,或者无法匹配的开头
if(k==-1 || par[j] == par[k])
{
// 此时保证j不能已经加过
next[j+1] = k + 1;
j++;
k++;
}
else
{
k = next[k];
}
}
}

int main()
{
scanf("%d", &t);
while(t--)
{
ans = 0;
scanf("%s", par);
scanf("%s", ori);

plen = strlen(par);
olen = strlen(ori);

if(olen==1 && plen==1)
{
if(ori[0] == par[0])
ans = 1;
else
ans = 0;

printf("%d\n", ans);
continue;
}

get_next(plen);
int j = 0;
for(long i=0; i<olen; i++)
{
// while的顺序:一定要先找到一个可以匹配的起点
// j>=1杜绝出现next[j]=-1,因此单独考虑 olen=plen=1
while(ori[i] != par[j] && j>=1)
{
j = next[j];
}

if(ori[i] == par[j])
{
j++;
}
// 在找到匹配的第一个字符前,i不能继续++
//			else
//			{
//				j = next[j];
//			}

if(j >= plen)
{
ans++;
// 回溯过程中不会遗漏,最差情况从头匹配
j = next[j];
}
}
printf("%d\n", ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  kmp hiho oj 北京大学