KMP算法及其实现

KMP算法是一种字符串匹配算法,它通过next数组的定义,优化了暴力匹配的时间复杂度。KMP算法的时间复杂度是O(M+N).

目录

一、算法原理

1. 计算next数组

2. 字符匹配过程

二、算法实现

三、算法复杂度分析


一、算法原理

1. 计算next数组

next[j]的计算方式为next[1~j]中的非平凡前缀和非平凡后缀元素一致的长度。

next数组的实现过程为:

i指针从1开始,j指针从0开始。每次索引元素时,用s[i]和s[j+1],这样做的好处是降低写代码的难度。

从第二个元素开始遍历(ne[1] = 0), 做以下操作:

(1)若不匹配,则一直循环j = ne[j];不匹配的条件是 j != 0 && s[j+1] != s[i]

(2)若匹配,则j ++; 匹配的条件是s[j+1] = s[i]

(3)执行完(1)(2)后,ne[i]即为j;

代码如下:

for(int i = 2, j = 0; i <= n; i ++ ){
     //(1)不匹配
     while(j && p[j + 1] != p[i]) j = ne[j];
            
     //(2)匹配
     if(p[j + 1] == p[i]) j ++ ;
           
     //(3)记录next
     ne[i] = j;
 }

2. 字符匹配过程

字符匹配过程与计算next数组的过程基本一致。

实现过程为:

设置两个指针,i指向长字符串,从1开始;j指向短字符串,从0开始。

从左往右遍历一次长字符串,每次遍历时:

(1)若不匹配,则一直循环j = ne[j],不匹配的条件是j && s[i] != p[j+1]

(2)若匹配,则j ++ ; 匹配的条件是s[i] == p[j + 1]

(3)若匹配完毕,则输出,并将j = ne[j]进行下一轮匹配

for(int i = 1, j = 0; i <= m; i ++ ){
     while(j && s[i] != p[j + 1]) j = ne[j];
     if(s[i] == p[j + 1]) j ++ ;
     if(j >= n){
         cout << i - n <<' ';
         j = ne[j];
     }
 }

二、算法实现

#include <iostream>
using namespace std;

const int N = 100010;
const int M = 1000010;
char s[N], p[M];
int ne[N];

int main(){
    int n,m;
    //1.扫描字符串
    cin >> n >> p + 1 >> m >> s + 1;
    
    //2.记录next
    for(int i = 2, j = 0; i <= n; i ++ ){
        //(1)不匹配
        while(j && p[j + 1] != p[i]) j = ne[j];
            
        //(2)匹配
        if(p[j + 1] == p[i]) j ++ ;
            
        //(3)记录next
        ne[i] = j;
    }
    
    for(int i = 1, j = 0; i <= m; i ++ ){
        while(j && s[i] != p[j + 1]) j = ne[j];
        if(s[i] == p[j + 1]) j ++ ;
        if(j >= n){
            cout << i - n <<' ';
            j = ne[j];
        }
    }
    return 0;
        
}

三、算法复杂度分析

设长字符串的长度为M, 短字符串的长度为N

则计算next数组算法的时间复杂度是O(N);

字符串匹配的时间复杂度是O(M);

总时间复杂度是O(M+N)

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前言: 本人的原创作品经典算法研究系列,自从10年12月末至11年12月,写了近一年。可以这么说,开博头俩个月一直在整理微软等公司的面试题,而后的四个月至今,则断断续续,除了继续微软面试100题系列,和程序员编程艺术系列之外,便在写这经典算法研究系列和相关算法文章。 本经典算法研究系列,涵盖A*.Dijkstra.DP.BFS/DFS.红黑树.KMP.遗传.启发式搜索.图像特征提取SIFT.傅立叶变换.Hash.快速排序.SPFA.快递选择SELECT等15个经典基础算法,共计31篇文章,包括算法理论的研究与阐述,及其编程的具体实现。很多个算法都后续写了续集,如第二个算法:Dijkstra 算法,便写了4篇文章;sift算法包括其编译及实现,写了5篇文章;而红黑树系列,则更是最后写了6篇文章,成为了国内最为经典的红黑树教程。 OK,任何人有任何问题,欢迎随时在blog上留言评论,或来信:[email protected]批评指正。谢谢。以下是已经写了的15个经典算法集锦,算是一个目录+索引,共计31篇文章: 十五个经典算法研究集锦+目录 一、A*搜索算法 一(续)、A*,Dijkstra,BFS算法性能比较及A*算法的应用 二、Dijkstra 算法初探 二(续)、彻底理解Dijkstra算法 二(再续)、Dijkstra 算法+fibonacci堆的逐步c实现 二(三续)、Dijkstra 算法+Heap堆的完整c实现源码 三、动态规划算法 四、BFS和DFS优先搜索算法 五、教你透彻了解红黑树 (红黑数系列六篇文章之其中两篇) 五(续)、红黑树算法实现与剖析 六、教你初步了解KMP算法、updated (KMP算法系列三篇文章) 六(续)、从KMP算法一步一步谈到BM算法 六(三续)、KMP算法之总结篇(必懂KMP) 七、遗传算法 透析GA本质 八、再谈启发式搜索算法 九、图像特征提取与匹配之SIFT算法 (SIFT算法系列五篇文章) 九(续)、sift算法的编译与实现 九(再续)、教你一步一步用c语言实现sift算法、上 九(再续)、教你一步一步用c语言实现sift算法、下 九(三续):SIFT算法的应用--目标识别之Bag-of-words模型 十、从头到尾彻底理解傅里叶变换算法、上 十、从头到尾彻底理解傅里叶变换算法、下 十一、从头到尾彻底解析Hash表算法 十一(续)、倒排索引关键词Hash不重复编码实践 十二、快速排序算法 (快速排序算法3篇文章) 十二(续)、快速排序算法的深入分析 十二(再续):快速排序算法之所有版本的c/c++实现 十三、通过浙大上机复试试题学SPFA 算法 十四、快速选择SELECT算法的深入分析与实现 十五、多项式乘法与快速傅里叶变换
以下是KMP算法及其改进算法(优化next数组)的C语言实现KMP算法: ```c void getNext(char *pattern, int *next) { int i = 0, j = -1; next[0] = -1; while (pattern[i]) { if (j == -1 || pattern[i] == pattern[j]) { i++; j++; next[i] = j; } else { j = next[j]; } } } int KMP(char *text, char *pattern) { int i = 0, j = 0; int text_len = strlen(text); int pattern_len = strlen(pattern); int *next = (int *)malloc(sizeof(int) * pattern_len); getNext(pattern, next); while (i < text_len && j < pattern_len) { if (j == -1 || text[i] == pattern[j]) { i++; j++; } else { j = next[j]; } } free(next); if (j == pattern_len) { return i - j; } else { return -1; } } ``` 改进算法(优化next数组): ```c void getOptimizedNext(char *pattern, int *next) { int i = 0, j = -1; next[0] = -1; while (pattern[i]) { if (j == -1 || pattern[i] == pattern[j]) { i++; j++; if (pattern[i] != pattern[j]) { next[i] = j; } else { next[i] = next[j]; } } else { j = next[j]; } } } int KMP_Optimized(char *text, char *pattern) { int i = 0, j = 0; int text_len = strlen(text); int pattern_len = strlen(pattern); int *next = (int *)malloc(sizeof(int) * pattern_len); getOptimizedNext(pattern, next); while (i < text_len && j < pattern_len) { if (j == -1 || text[i] == pattern[j]) { i++; j++; } else { j = next[j]; } } free(next); if (j == pattern_len) { return i - j; } else { return -1; } } ``` 在上面的示例代码中,KMP算法中的getNext函数用来计算next数组,KMP函数用来执行KMP算法的主体部分。改进算法中的getOptimizedNext函数用来计算优化后的next数组,KMP_Optimized函数用来执行优化后的KMP算法。这些函数可以根据需要修改函数参数和返回值类型以适应实际情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值