KMP模式匹配算法示例

本来KMP算法的实现没几行代码,但是因为要满足题目要求的“显示每趟匹配过程”,而定义了很多花里胡哨的函数。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAXLEN 255
typedef struct {
	char ch[MAXLEN+1];
	int length;
}SString;
int next[MAXLEN + 1];
int nextval[MAXLEN + 1];
int per[MAXLEN + 1] = { 0 }, total[MAXLEN + 1] = { 0 };//用来计算模式串各个字符参与匹配的次数
int main()
{
	void exam(SString father, SString son);						//检验主串和模式串是否正常
	void menu();												//输出菜单栏
	void BF(SString father, SString son, int K);				//BF算法
	void KMP(SString father, SString son, int K);				//KMP算法
	void KMPVAL(SString father, SString son, int K);			//KMP修正算法
	SString father, son;
	int K;//开始匹配的位置
	int af, p;//p从串最后一个位置移动到串的第一个位置,af永远在p的后一位,用这两个变量来让ch[1]变成第一个元素,以此类推
	int choose;//储存用户在菜单栏输入的选项
	menu();
	while (1) {
		printf("请输入数字0—4来选择菜单项,其它输入则不起作用。\n\n");
		scanf("%d", &choose);
		if ((choose < 0) || (choose > 4)) {
			printf("输入的数据无效!\n");
			exit(0);
		}
		printf("收到\n");
		switch (choose){
			case 0:
				printf("成功退出管理系统\n");
				exit(0);
				break;
			case 1:
				printf("请依次输入主串、子串和匹配起始位置,中间用空格键隔开:\n");
				scanf("%s %s %d", father.ch, son.ch, &K);//输入字符串给father.ch和son.ch
				father.length = strlen(father.ch);//计算主串长度并赋值给father.length
				af = father.length;					/********************************/
				for (p = af - 1; p >= 0;) {			/*主串字符向后串一位,		    */
					father.ch[af] = father.ch[p];	/*使得father.ch[i]成为主串的	*/
					af--; p--;						/*第i个元素						*/
				}									/********************************/
				son.length = strlen(son.ch);//计算模式串长度并赋值给son.length
				af = son.length;				/********************************/
				for (p = af - 1; p >= 0;) {		/*模式串字符向后串一位,	    */
					son.ch[af] = son.ch[p];		/*使得son.ch[i]成为模式串的	    */
					af--; p--;					/*第i个元素						*/
				}								/********************************/

				break;
			case 2:
				exam(father,son);
				BF(father, son, K);
				break;
			case 3:
				exam(father, son);
				KMP(father, son, K);
				break;
			case 4:
				exam(father, son);
				KMPVAL(father,son,K);
				break;
			default:
				break;
		}
	}
	system("pause");
	return 0;
}


void exam(SString father, SString son) {
	if (!father.ch && !father.length && !son.ch && !son.length) {
		printf("输入的数据无效!\n");//若主串或模式串中有数据没有成功输入,则退出程序
		exit(0);
	}
}

void menu()
{
	printf("1.输入主串、子串和匹配起始位置\n");
	printf("2.朴素的模式匹配算法\n");
	printf("3.KMP改进算法(Next[])\n");
	printf("4.KMP改进算法(NextVal[])\n");
	printf("0.退出管理系统\n");

}
//输入一个存有字符串的结构体,输出该字符串并自动换行
void output(SString S) {
	int i;
	for (i = 1; i <= S.length; i++) {
		putchar(S.ch[i]);
	}
	putchar('\n');
} 
//输出整型数组per[],且只输出从per[1]到per[j]部分,并换行(一般用来输出各字符的匹配次数)
void put_frequency(int per[],int j) {
	int t;
	for (t = 1; t <= j; t++)//输出模式串各字符在该次匹配中,参与匹配的次数。未输出的部分未匹配。
	{
		printf("%d", per[t]);
		per[t] = 0;
	}
	putchar('\n');
}
//输出每一次匹配的过程
void process_compete(SString father,int i,SString son,int j,int len_comp) {
	int t,blank;
	void output(SString S);
	output(father);
	for (blank = i - 1 - len_comp; blank > 0; blank--)
	{
		putchar(' ');
	}
	for (t = 1; t <= j; t++)
	{
		putchar(son.ch[t]);
	}								
	putchar('\n');
}

void get_next(SString T, int next[]) {
	int i,j;
	i = 1;
	j = 0;
	next[0] = 1024;
	next[1] = 0;
	while (i<T.length) {
		if (j == 0 || T.ch[i] == T.ch[j]){
			i++; j++;
			next[i] = j;
		}
		else {
			j = next[j];
		}
	}
}
void get_nextval(SString T, int nextval[])
	{ 
		int i,j;
		i = 1;
		j = 0;
		nextval[1] = 0;
		while (i < T.length)
		{
			if (j == 0 || T.ch[i] == T.ch[j]) {
				++i; ++j;
				if (T.ch[i] != T.ch[j])
					nextval[i] = j;
				else
					nextval[i] = nextval[j];
			}
			else
				j = nextval[j];
		}
	}
void BF(SString S, SString T, int K)
{
	//void output(SString S);//输入一个存有字符串的结构体,输出该字符串并自动换行
	void process_compete(SString father, int i, SString son, int j, int len_comp);
	void put_frequency(int per[], int j);
	int i, j, result, num = 0;//i记录主串匹配的位置,j记录模式串匹配的位置,result存储匹配成功的位置,num存储匹配次数

	//只需要i、j两个局部变量就可以实现BF算法,其余的变量只是为了展现模式匹配的过程

	int len_comp = 0;//len_comp存储已经匹配成功的部分模式串长度
	int blank;//blank存储需要空多少个空格字符
	i = K; j = 1;
	printf("-----------------------------------------------\n");
	while (i <= S.length && j <= T.length) {
		//只要模式串中该位置参与了匹配,就记一次数,并在同一循环中输出、清空
		per[j]++;
		total[j] = total[j] + per[j];
		printf("%d", per[j]);
		per[j] = 0;

		if (S.ch[i] == T.ch[j]) {
			i++; j++;
			len_comp++;
		}
		else {
			num++;
			printf("\n第 %d 次匹配:\n", num);//若n次匹配后成功,那么前n-1次匹配都是失败的
			//output(S);
			process_compete(S, i, T, j, len_comp);
			printf("-----------------------------------------------\n");
			i = i - j + 2;
			j = 1;
			len_comp = 0;
		}
	}
	printf("\n第 %d 次匹配:\n", ++num);
	process_compete(S, i, T, j - 1, len_comp);
	printf("-----------------------------------------------\n");
	if (j > T.length) {
		result = i - T.length;
		printf("\n匹配成功,位置序号为:%d\n", result);
	}
	else {
		result = 0;
		printf("匹配失败\n");
	}
	printf("模式串总匹配次数:\n");//无论是否成功匹配,都输出总的匹配次数
	put_frequency(total, T.length);
	printf("匹配总趟数:%d\n", num);
}
void KMP(SString father, SString son, int K) {
	//void output(SString S);//输入一个存有字符串的结构体,输出该字符串并自动换行
	void get_next(SString T, int next[]);
	void put_frequency(int per[], int j);
	int i, j, result, num = 0;//i记录主串匹配的位置,j记录模式串匹配的位置,result存储匹配成功的位置,num存储匹配次数

	//只需要i、j两个局部变量就可以实现BF算法,其余的变量只是为了展现模式匹配的过程

	int len_comp = 0;//len_comp存储已经匹配成功的部分模式串长度
	int blank;//blank存储需要空多少个空格字符
	get_next(son, next);
	{															//首先输出next[]各元素的数值
		int pose_next;
		printf("在KMP算法中,next数组是:\n");
		for (pose_next = 1; pose_next <= son.length; pose_next++) {
			printf("%d", next[pose_next]);
		}
		putchar('\n');
	}
	i = K;
	j = 1;
	printf("-----------------------------------------------\n");
	while (i <= father.length && j <= son.length) {
		if (j) {
			per[j]++;
			total[j] = total[j] + per[j];
		}
		if (j == 0 || father.ch[i] == son.ch[j]) {
			i++; j++;
			len_comp++;
		}
		else {
			num++;//若n次匹配后成功,那么前n-1次匹配都是失败的,
			printf("第 %d 次匹配:\n", num);//因此在循环体内只对匹配失败计数
			put_frequency(per, j);
			process_compete(father, i, son, j, len_comp);
			len_comp = next[j] - 1;
			j = next[j];
			printf("-----------------------------------------------\n");
		}
	}
	printf("第 %d 次匹配:\n", ++num);//最后一次匹配
	put_frequency(per, j - 1);
	process_compete(father, i, son, j - 1, len_comp);
	printf("-----------------------------------------------\n");
	if (j > son.length) {
		result = i - son.length;
		//printf("第 %d 次匹配:\n", ++num);//最后一次匹配成功
		//output(father);
		//for (blank = i - j; blank > 0; blank--)
		//	putchar(' ');
		//output(son);
		printf("匹配成功,位置序号为:%d\n", result);

	}
	else {
		result = 0;
		printf("匹配失败\n");
	}
	printf("总匹配次数:\n");
	put_frequency(total, son.length);
	printf("匹配总趟数:%d\n", num);
}
void KMPVAL(SString father, SString son, int K) {
	//void output(SString S);//输入一个存有字符串的结构体,输出该字符串并自动换行
	void get_nextval(SString T, int nextval[]);
	void put_frequency(int per[], int j);
	int i, j, result, num = 0;//i记录主串匹配的位置,j记录模式串匹配的位置,result存储匹配成功的位置,num存储匹配次数

	//只需要i、j两个局部变量就可以实现BF算法,其余的变量只是为了展现模式匹配的过程

	int len_comp = 0;//len_comp存储已经匹配成功的部分模式串长度
	int blank;//blank存储需要空多少个空格字符
	get_nextval(son, nextval);
	{															//首先输出next[]各元素的数值
		int pose_next;
		printf("在KMPVAL算法中,nextval数组是:\n");
		for (pose_next = 1; pose_next <= son.length; pose_next++) {
			printf("%d", nextval[pose_next]);
		}
		putchar('\n');
	}
	i = K;
	j = 1;
	printf("-----------------------------------------------\n");
	while (i <= father.length && j <= son.length) {
		if (j) {
			per[j]++;
			total[j] = total[j] + per[j];
		}
		if (j == 0 || father.ch[i] == son.ch[j]) {
			i++; j++;
			len_comp++;
		}
		else {
			num++;							//若n次匹配后成功,那么前n-1次匹配都是失败的,
			printf("第 %d 次匹配:\n", num);//因此在循环体内只对匹配失败计数
			put_frequency(per, j);
			process_compete(father, i, son, j, len_comp);
			len_comp = nextval[j] - 1;
			j = nextval[j];
			printf("-----------------------------------------------\n");
		}
	}
	printf("第 %d 次匹配:\n", ++num);//最后一次匹配
	put_frequency(per, j - 1);
	process_compete(father, i, son, j - 1, len_comp);
	printf("-----------------------------------------------\n");
	if (j > son.length) {
		result = i - son.length;
		printf("匹配成功,位置序号为:%d\n", result);

	}
	else {
		result = 0;
		printf("匹配失败\n");
	}
	printf("总匹配次数:\n");
	put_frequency(total, son.length);
	printf("匹配总趟数:%d\n", num);
}



猜你喜欢

转载自blog.csdn.net/weixin_45720773/article/details/106803454
今日推荐