【数据结构】KMP算法

/KMP算法/

sample in:
abckfbwejbwcv
ckf
sample out:
3

对于代码的理解参考下面定义:
next数组的定义:
对于每模式串 t 的每个元素 t j t_j tj,都存在一个实数 k ,使得模式串 t 开头的 k 个字符( t 0 t_0 t0 t 1 t_1 t1 t k − 1 t_{k-1} tk1)依次与 t j t_j tj前面的 k个字符( t j − k t_{j-k} tjk t j − k + 1 t_{j-k+1} tjk+1 t j − 1 t_{j-1} tj1),这里第一个字符 t j − k t_{j-k} tjk 最多从 t 1 t_1 t1 开始,所以 k < j)个字符相同。如果这样的 k 有多个,则取最大的一个。模式串 t 中每个位置 j 的字符都有这种信息,采用 next 数组表示,即 n e x t [ j ] = M A X { k } next[ j ]=MAX\{ k \} next[j]=MAX{k}
匹配过程:
根据 next 数组的定义得知 “ t k t_k tk ~ t j − 1 t_{j-1} tj1 ” == “ t 0 t_0 t0 ~ t k − 1 t_{k-1} tk1”,所以 “ t 0 t_0 t0 ~ t k − 1 t_{k-1} tk1” == “ s i − k s_{i-k} sik ~ s i − 1 s_{i-1} si1

具体匹配过程可参考博客:
https://blog.csdn.net/dark_cy/article/details/88698736
https://www.cnblogs.com/dusf/p/kmp.html

#include<stdio.h>
#include<string.h>
#include<malloc.h>
#define string_size 200//该程序定义的字符串最大长度 
#define error 1
#define ok 0
typedef char elem;
typedef int status;
typedef struct string
{
 	elem *ch;
 	int length;//字符串长度 
}string;//定义一个字符串结构体 
void get_next(elem t[],int *next)
{
 	int j=0,k=-1;//j作为遍历字符串的循环变量,k作为计数变量 
 	next[j]=k;//首先对next初始化为-1
 	while(t[j]!='\0')
 	{
	  	if(k==-1||t[j]==t[k])
	  	{
	   		j++;
	   		k++;
	   		next[j]=k;
	  	}//如果能匹配上则将k值赋给next[j]同时k值加一 ,此时k是相同子串的长度 
	  	else k=next[k];//如果不能匹配上,k回退。ps:但是j在继续向前走 
 	}
}//求模式串next值的函数 
/*	
	例如求   a b c a b c的next值,
	结果为:-1 0 0 0 1 2 
*/
status kmp(elem a[],elem b[]) 
{
	int next[string_size];
	get_next(b,next);
	int i=0,j=0;
	int lens=strlen(a),lensub=strlen(b);
	while(i<lens&&j<lensub)
	{
		if(j==-1||a[i]==b[j])
		{
			i++;
			j++;
		}
		else 
		{
			j=next[j];
		}
	}//用next数组模拟子串前进的过程 
	if(j>=lensub)
	return i-j;//匹配成功则返回子串的位置 
	return -1;
}
int main()
{
 	elem a[string_size],b[string_size];
 	while(scanf("%s%s",a,b)!=EOF)
	{
 		printf("%d\n",kmp(a,b)+1);
	}
}

/改进的kmp算法/

sample in:
abckfbwejbwcv
ckf
sample out:
3

求nextval数组的方法与改进前的kmp有不同,但是匹配方式是相同的

j==-1表示i指向的字符串与j指向的字符串从第一个字符就不等i,j均后移,j==-1表示指向第一个字符之前

改进的KMP算法又添加了一个数组nextval, 它是在next基础之上计算出来的。
在求next数组的值的时候,判断一下,如果这个字符的next[j]=k,则next[j+1]不是直接等于k+1,而是判断一下,如果,j+1的字符等于k+1的字符,那么next[j+1]=next[k+1]。

#include<stdio.h>
#include<string.h>
#include<malloc.h>
#define string_size 200
#define error 1
#define ok 0
typedef char elem;
typedef int status;
typedef struct string
{
	elem *ch;
	int length;
}string;
void get_nextval(char n[],int nextval[])
{
	int len=strlen(n);
	int i=0;
	int k=-1;
	nextval[i]=k;
	while(i<len)
	{
		if(k==-1||n[k]==n[i])
		{
			i++;
			k++;
			if(n[k]==n[i])
			{
				nextval[i]=nextval[k];
			}
			else if(i<len)
			{
				nextval[i]=k;
			}
		}
		else
		{
			k=nextval[k];
		}
	}
	i=0;
	while(n[i]!='\0')
	{
		printf("%d ",nextval[i]); 
		i++;
	}
	printf("\n");
}
status kmp(elem a[],elem b[]) 
{
	int next[string_size];
	get_nextval(b,next);
	int i=0,j=0;
	int lens=strlen(a),lensub=strlen(b);
	while(i<lens&&j<lensub)
	{
		if(j==-1||a[i]==b[j])
		{
			i++;
			j++;
		}
		else 
		{
			j=next[j];
		}
	}//用next数组模拟子串前进的过程 
	if(j>=lensub)
	return i-j;//匹配成功则返回子串的位置 
	return -1;
}
int main()
{
 	elem a[string_size],b[string_size];
 	while(scanf("%s%s",a,b)!=EOF)
	{
 		printf("%d\n",kmp(a,b)+1);
	}
	return 0;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值