KMP算法C++实现

看了灯笼的讲解,形象生动的体会了KMP算法的奥妙,参考其思路用C++写了下来,关于KMP算法的讲解在代码注释中。

我按照自己的理解把算法的分为以下步骤(text为长字符串,pattern为和text匹配的短字符串):

  1. 列出pattern所有的前缀。比如“ABABCAABAB”,能够列出10种前缀(前缀的具体定义可以参照龙书第二章),
  2. 构建前缀表。找出每个前缀中前后能够匹配的字符串长度,例如“ABABCAAB”,只有首尾"AB"能够匹配,所以前缀表相应位置为2。
  3. 开始进行字符串的匹配。简单说,字符串匹配的时候,text和pattern的下标都加1(pattern长度等于1时只移动text的下标);如果不匹配,则根据前缀表进行pattern的移动再从移动后的位置开始匹配;
#include<stdio.h>
#include<iostream>
#include<string>
#include<stdlib.h>
using namespace std;

// 求前缀表
void prefix_table(char pattern[], int prefix[], int n) {
	// 一开始规定好的
	prefix[0] = 0;
	int len = 0;
	// 检测第i个字母
	int i = 1;
	// 主要讲第一次循环,如果i=1,len=0且pattern[0]!=pattern[1]的时候,当不匹配时我们会侧向选择,即 len = prefix[len-1]
	// 但是在字符串的开头 prefix[len-1]是会越界的,所以应该直接让prefix[i] = len就好,然后继续向前匹配
	while (i < n) {
		if (pattern[i] == pattern[len]) {
			len++;
			prefix[i] = len;
			i++;
		}
		else {
			if (len > 0) {
				len = prefix[len - 1];
			}
			else {
				prefix[i] = len;
				i++;
			}
		}
	}
}
// 前缀表移动
void move_prefix_table(int prefix[], int n) {
	// 生成的前缀表并不是我们想要的前缀表,我们要把前缀表有的向后移动,然后prefix[0]=-1
	for (int i = n-1; i>0; i--) {
		prefix[i] = prefix[i - 1];
	}
	prefix[0] = -1;
}

// KMP算法
void kmp_search(char text[], char pattern[]) {
	int n = strlen(pattern);
   	int* prefix = new int[n];
	prefix_table(pattern,prefix, n);
	move_prefix_table(prefix, n);
	int j = 0;
	int i = 0;
	int m = strlen(text);
	// 做好前期各个变量的准备,主要是text,pattern的长度以及i,j两个下标
	//text[i]   len(text) = m;
	// pattern[j]  len(pattern) = n;
	// 开始遍历text
	while (i < m) {
		// 如果j到了pattern的尽头且最后一位也匹配了,说明当前位置就是匹配到的字符串
		if (j == n-1 && pattern[j] == text[i]) {
			printf("Find pattern at %d\n", i - j);
			// 为了匹配所有的pattern,设置j = prefix[j]继续匹配
			j = prefix[j];
			if (j == -1) {
				j = 0;
			}
		}
		// 如果字符匹配,直接ij向后移动.再考虑一种情况,如果当前匹配了,但是pattern长度为1,如都i++,j++那么j的读取会越界
		// 所以我们要加一个判断,如果j==n-1,则只移动i,这是针对pattern为1的情况
		if (text[i] == pattern[j]) {
			if (j == n - 1) {
				i++;
			}
			else {
				i++; j++;
			}
		}
		// 如果当前ij下pattern和text不匹配
		else {
			// 则令 j = prefix[j]
			j = prefix[j];
			// 如果这时候在字符串的开头,两者都不匹配的情况,只能将pattern向text的下一位匹配,即i,j各加1
			if (j == -1) {
				i++; j++;
			}
		}
	}
}
int main() {
    //第一个样例,匹配的字符串长度为1
	char pattern[] = "A";
	char text[] = "ABABABCABAABABABAB";
	kmp_search(text, pattern);	
    // 第二个测试样例,匹配的字符串长度大于1
	char pattern1[] = "AB";
    kmp_search(text,pattern1);
	system("pause");
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值