字符串 - KMP算法

字符串算法中,字符串匹配是一个非常重要的应用。例如在网页中查找关键词,其实就是在对字符串匹配,也就是看一个主字符串中是否包含了一个子字符串。

而KMP算法在字符串匹配方法中一个很著名并且很聪明的算法,当然也确实比较难理解。甚至于有程序员因为无法理解KMP算法而直接改用暴力匹配。本身自己学算法起步较晚,第一次接触到KMP算法已经是研究生毕业一年了。虽然带着研究生的学历背景,但是刚开始看的时候依然是一脸懵逼。看了很多博主的讲解总算是明白了,所以在这篇博客中记录下来自己的理解,如果能帮助到别人也是万分荣幸了。本篇博客参考了孤~影~关于KMP算法的讲解

先从暴力匹配方法感受一下字符串匹配的过程。其中i表示匹配过程中主字符串的位置,j表示子字符串的位置。可以看出,j总是从0开始匹配,而i也是从0开始,一步一步向后移。

注意上面的算法,是i和j同时在移动,如果遇到不匹配的,那么i和j同时回退。这种算法简单粗暴好理解,但是仍然有改进的地方。

以上图为例,想象一下是自己手动拿着P去匹配S,假如第一轮没匹配上,那么我们很自然想到直接从第一轮跳到第四轮,因为第二轮和第三轮的首字母A明显就和S中的字符B、C不符。

再来几个例子感受下这个更快速的移动过程,让我们试着发现一些规律。

上面三个例子中移动位置其实是比较理想的,假想是我们人为去匹配多半也是这样比划的。那么观察一下红框中的字符串,其实存在一定的规律。

其实在这些情况下之所以存在比暴力算法更快的匹配,就是因为子字符串本身就包含了一定的特殊性。我们可以从上图中看出,子字符串的首和尾其实存在着重复的字符串。更具一般性的,

子字符串P中的,P[0] ~ P[k-1] = P[j-k] ~ P[j-1]。也就是首尾重复字符串,注意也是最大的首尾重复字符串。注意以下一种情况。

以上就是为了说明,假如我们遇到了第j个字符不匹配时,可以直接移动到k,而不需要比较前k-1个字符。

于是现在的重点就在于怎么求k。

1)next数组是什么?

next数组记录了子字符串遇到不匹配时要回退的位置。上述举的例子恰好是P数组最后一个字符不匹配,但实际上不匹配可能出现在P的任何一个位置。因此我们为长度为n的P字符串建立一个长度也为n的next数组。next[j]表示,当P的第j个字符出现不匹配时,字符匹配子字符串应该移动到的位置,如下图所示。

具体来说,以"abbaabcab"为例,假设要计算next[6]。那么对于"abbaab"来说:

头部有: a ab abb abba abbaa(不包含最后一个字符)

尾部有: b ab aab baab bbaab (不包含第一个字符)

最长首尾相同字符串就是ab,长度为2。那么next[6] = 2。

2)如何求next数组?

假设k表示当前最大首尾相同字符串的头部,j指向

  • 初始时刻,k指向-1,j指向0的位置。

  • 当遇到首尾相同的字符时,k和j都向右移动一位

  • 当遇到首尾不同的字符时,k向左回退到next[k]

    也就是说,当P[k]不等于P[j]时,k必须要回退到次大的首尾相同字符串,也就是上图中的黄色块。

结合代码可能更清楚。求next数组的代码:

public static int[] getNext(String ps) {

    char[] p = ps.toCharArray();

    int[] next = new int[p.length];

    next[0] = -1;

    int j = 0;

    int k = -1;

    while (j < p.length - 1) {

       if (k == -1 || p[j] == p[k]) {

           next[++j] = ++k;

       } else {

           k = next[k];

       }
    }
    return next;
}

求得了next数组之后,就可以写KMP算法了:

public static int KMP(String ts, String ps) {

    char[] t = ts.toCharArray();

    char[] p = ps.toCharArray();

    int i = 0; // 主串的位置

    int j = 0; // 模式串的位置

    int[] next = getNext(ps);

    while (i < t.length && j < p.length) {

       if (j == -1 || t[i] == p[j]) { // 当j为-1时,要移动的是i,当然j也要归0

           i++;

           j++;

       } else {
           j = next[j]; // j回到指定位置
       }
    }

    if (j == p.length) {

       return i - j;

    } else {
       return -1;
    }
}

以上就是KMP算法的原理了,总结一下:

1)KMP算法比暴力匹配改进的地方其实只在于当子字符串本身存在着一定的首尾相同的情况时,可以直接跳到头部字符串的末端。

2)KMP算法保证了主字符串的位置i不回退,而只回退子字符串的位置j,而且j不需要每次都从0开始。

参考资料: 《算法》第四版

      https://www.cnblogs.com/yjiyjige/p/3263858.html

      

原文地址:https://www.cnblogs.com/corineru/p/10807173.html

时间: 2024-08-26 16:02:31

字符串 - KMP算法的相关文章

Chapter 4. 字符串 KMP算法

Chapter 4. 字符串 KMP算法 Sylvia's I. 讲的比较好的博客1,博客2 表问窝,在窝翻遍全网的博客后,窝已经处于混乱状态-- Sylvia's II. 窝是贴代码的小能手-- //前方高能请注意//窝并不知道窝在说什么--#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> using namespa

字符串kmp算法详解

之前要研究aho-corasick算法 拖了好久  感觉自己博客要开始了!! aho-corasick算法依赖2元素: 1.Trie树解析,1个月前就已经写过博客分析过了. 2.KMP算法 此文重点介绍字符串KMP算法: 一开始说说普通模式算法("BF"算法)思路:模式串从主串的第一个字符开始匹配,每匹配失败,主串中记录匹配进度的指针 i 都要进行 i-j+1 的回退操作(这个过程称为"指针回溯"),同时模式串向后移动一个字符的位置.一次次的循环,直到匹配成功或者程

hdu 2594 java实现字符串KMP算法

Problem Description Homer: Marge, I just figured out a way to discover some of the talents we weren't aware we had. Marge: Yeah, what is it? Homer: Take me for example. I want to find out if I have a talent in politics, OK? Marge: OK. Homer: So I tak

字符串的KMP算法替换

1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 6 7 class myString 8 { 9 private: 10 string mainstr; 11 int size; 12 void GetNext(string p,int next[]); 13 int KMPFind(string p,int next[]); 14 public: 15 myString(); 16 //~myS

字符串模式匹配KMP算法中的next数组算法及C++实现

一.问题描述: 对于两个字符串S.T,找到T在S中第一次出现的起始位置,若T未在S中出现,则返回-1. 二.输入描述: 两个字符串S.T. 三.输出描述: 字符串T在S中第一次出现的起始位置,若未出现,则返回-1. 四.输入例子: ababaababcbababc 五.输出例子: 5 六.KMP算法解析: KMP算法分为两步,第一步是计算next数组,第二步是根据next数组通过较节省的方式回溯来比较两个字符串. 网络上不同文章关于next数组的角标含义略有差别,这里取参考文献中王红梅<数据结构

Java数据结构之字符串模式匹配算法---KMP算法

本文主要的思路都是参考http://kb.cnblogs.com/page/176818/ 如有冒犯请告知,多谢. 一.KMP算法 KMP算法可以在O(n+m)的时间数量级上完成串的模式匹配操作,其基本思想是:每当匹配过程中出现字符串比较不等时,不需回溯指针,而是利用已经得到的"部分匹配"结果将模式向右"滑动"尽可能远的一段距离,继续进行比较.显然我们首先需要获取一个"部分匹配"的结果,该结果怎么计算呢? 二.算法分析 在上一篇中讲到了BF算法,

Java数据结构之字符串模式匹配算法---KMP算法2

直接接上篇上代码: 1 //KMP算法 2 public class KMP { 3 4 // 获取next数组的方法,根据给定的字符串求 5 public static int[] getNext(String sub) { 6 7 int j = 1, k = 0; 8 int[] next = new int[sub.length()]; 9 next[0] = -1; // 这个是规定 10 next[1] = 0; // 这个也是规定 11 // 12 while (j < sub.l

字符串与模式匹配(一)&mdash;&mdash;KMP算法

源码:kmp.cpp // KMP.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <IOSTREAM> using namespace std; #define BUF_SIZE 100 #define BUF_SIZE_2 200 /*****************************************************

字符串模式匹配之KMP算法图解与 next 数组原理和实现方案

之前说到,朴素的匹配,每趟比较,都要回溯主串的指针,费事.则 KMP 就是对朴素匹配的一种改进.正好复习一下. KMP 算法其改进思想在于: 每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 i指针,而是利用已经得到的“部分匹配”的结果将模式子串向右“滑动”尽可能远的一段距离后,继续进行比较.如果 ok,那么主串的指示指针不回溯!算法的时间复杂度只和子串有关!很好. KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的,很自然的,需要一个函数来存储匹