kmp算法

这篇文章的确写得不错:http://blog.youkuaiyun.com/v_JULY_v/article/details/6111565

这篇更是不错:http://blog.youkuaiyun.com/v_JULY_v/article/details/6545192

这次学习kmp算法还是太匆忙,严蔚敏的书上讲得和算法导论有些不同,应该要研究下

这道题目:http://hzzy-010.blog.163.com/blog/static/796923812009914365725/应该也要看看

严蔚敏书上讲得更清晰明了,有时间要研究

下面是我自己的一些想法


上图证明了:

a0a1...ak-1ak=aj-kaj-k+1...aj-1aj
则对于pattern的前j+1序列字符,则有如下可能
⑴     pattern[k+1]==pattern[j+1] 此时overlay(j+1)=k+1=overlay(j)+1
⑵     pattern[k+1]≠pattern[j+1] 此时只能在pattern前k+1个子符组所的子串中找到相应的overlay函数,h=overlay(k),如果此时pattern[h+1]==pattern[j+1],则overlay(j+1)=h+1否则重复(2)过程.



上图是kmp大体的思想。。。。当然这是草图,具体思想,还得看程序:


kmp最难理解的就是next数组的含义,一旦理解之后,算法就理解了


对于模式

0  1  2  3  4  5

a  b  a  a  b  c

next[5]的含义指,以下标4为结尾的后缀子串与 以下标next[5]-1为结尾的前缀是相同的。

next[5] = 2

以下标4结尾的后缀ab 和以下标2-1结尾的前缀ab是相同的


#include <string>
#include <iostream>
#include <vector>

using namespace std;


int kmp(const string &t,const string &p)
{
	//首先要对模式p预处理
	vector<int> next(p.size());
	next[0] = -1;
	for (int i=1;i<p.size();++i)//求next[i]
	{
		int pre = next[i-1];//先求出段[0,i-1]前缀和后缀最大的相同,即[0,k] = [i-1-k,i-1],使得k最大;
		while (pre >=0 && p[pre+1]!=p[i])
		{
			pre = next[pre];
		}
		if(p[pre+1]==p[i])
			next[i] = pre +1;
		else
			next[i] = -1;
	}

	//查找
	int tbeg=0,pbeg = 0;
	while (tbeg<t.size() && pbeg<p.size())
	{
		if(t[tbeg]!=p[pbeg])
		{
			if(pbeg==0)
				++tbeg;
			else
			{
				pbeg = next[pbeg -1] +1;//next[i]表示的是在模式串中相同的最大的前缀的结束的下标;因为此时t[tbeg]!=p[pbeg]
			}                               //先找出j = next[pbeg-1],使得[0,j] = [pbeg -1-j,pbeg-1];所以[0,j]不需在比较,要比较
		}                                       //j+1
		else
		{
			++tbeg;
			++pbeg;
		}
	}

	if(pbeg ==p.size())
		return tbeg - p.size();
	return -1;


}

int main()
{
	string source = "annbcdanacadsannannabnna";
	string pattern = "anna";
	cout<<kmp(source,pattern)<<endl;
	return 0;
}


更为简洁效率更高的KMP

void getNext(char *p,vector<int> & next)
{
   int len = strlen(p) - 1;
   int j = 0;
   int k = -1;
   next[0] = -1;
   while (j < len) {
       if (k == -1 ||p[j] == p[k]) {
           ++k;
           ++j;
           next[j] = k ;
       }
       else
           k = next[k];
   }
}

int KMP(char *src , char *p) {
    int pLen = strlen(p);
    int sLen = strlen(src);
    vector<int> next(pLen,0);
    getNext(p,next);
    
    int i = 0;
    int j = 0;

    while (i < sLen ) {
        if (j == -1 || src[i] == p[j]) {
            ++i;
            ++j;
        }
        else
            j = next[j];

        if (j == pLen)
            return i - j;
    }
   
    return -1;
}

int main()
{
	cout<< KMP("acabaabaabcacaabc","abaabc");

	return 0;
}



### KMP算法的实现与原理 KMP(Knuth-Morris-Pratt)算法是一种高效的字符串匹配算法,其核心思想是通过构建部分匹配表(也称为`next`数组或`failure`函数),避免在模式串匹配失败时对文本串进行回溯,从而降低时间复杂度[^1]。 #### 一、KMP算法的核心原理 KMP算法的关键在于利用模式串的部分匹配信息,构建一个最长公共前后缀表(通常称为`next`数组)。当匹配失败时,算法根据`next`数组中的值跳转到适当的位置继续匹配,而是简单地将文本串指针回退。这种方法确保了文本串只需被扫描一次,时间复杂度为 \(O(n + m)\),其中 \(n\) 是文本串长度,\(m\) 是模式串长度[^3]。 #### 二、部分匹配表(`next`数组)的构造 `next`数组记录了模式串中每个位置对应的最长公共前后缀长度。例如,对于模式串 `"ABABC"`,其`next`数组为 `[0, 0, 1, 2, 0]`。以下是构造`next`数组的步骤: ```python def compute_next(pattern): n = len(pattern) next_array = [0] * n j = 0 # 前缀末尾索引 for i in range(1, n): # 后缀末尾索引从1开始 while j > 0 and pattern[i] != pattern[j]: j = next_array[j - 1] if pattern[i] == pattern[j]: j += 1 next_array[i] = j return next_array ``` #### 三、KMP算法的实现 基于上述`next`数组,可以实现高效的字符串匹配。以下是完整的KMP算法实现代码: ```python def kmp_search(text, pattern): next_array = compute_next(pattern) # 构造next数组 n, m = len(text), len(pattern) j = 0 # 模式串指针 for i in range(n): # 遍历文本串 while j > 0 and text[i] != pattern[j]: j = next_array[j - 1] if text[i] == pattern[j]: j += 1 if j == m: # 匹配成功 return i - m + 1 # 返回匹配起始位置 return -1 # 匹配失败 ``` #### 四、示例分析 假设文本串为 `"ABABABCABABC"`, 模式串为 `"ABABC"`,则通过KMP算法可以快速找到模式串在文本串中的首次出现位置。具体过程如下: 1. 初始化文本串指针 `i = 0` 模式串指针 `j = 0`。 2. 依次比较字符,若匹配失败,则根据`next`数组调整模式串指针 `j`。 3. 当模式串完全匹配时,返回匹配起始位置。 #### 五、C语言实现示例 除了Python,KMP算法也可以用C语言实现。以下是一个简单的C语言版本: ```c #include <stdio.h> #include <string.h> void computeNext(const char* pattern, int* next, int m) { int j = 0; next[0] = 0; for (int i = 1; i < m; i++) { while (j > 0 && pattern[i] != pattern[j]) { j = next[j - 1]; } if (pattern[i] == pattern[j]) { j++; } next[i] = j; } } int kmpSearch(const char* text, const char* pattern) { int n = strlen(text); int m = strlen(pattern); int next[m]; computeNext(pattern, next, m); int j = 0; for (int i = 0; i < n; i++) { while (j > 0 && text[i] != pattern[j]) { j = next[j - 1]; } if (text[i] == pattern[j]) { j++; } if (j == m) { return i - m + 1; } } return -1; } int main() { const char* text = "ABABABCABABC"; const char* pattern = "ABABC"; int result = kmpSearch(text, pattern); printf("Pattern found at index: %d\n", result); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值