当前位置: 首页 > news >正文

算法之美--3.2.3 KMP算法

不知道看了几遍的kmp,反正到现在都没有弄清楚next[j]的计算和kmp的代码实现,温故而知新,经常回来看看,相信慢慢的就回了 

 从头到尾彻底理解KMP

理解KMP

/*!
* \file KMP_算法.cpp
*
* \author ranjiewen
* \date 2017/02/12 16:12
*
*/

void preKmp(const char* pattern, int m, int kmpNext[])  //和GetNextval等价
{
    int i, j;
    i = 0;
    j = kmpNext[0] = -1;
    while (i<m)
    {
        while (j>-1 && pattern[i] != pattern[j]) // i为后缀,j为前缀
        {
            j = kmpNext[j];
        }
        i++;
        j++;
        if (pattern[i] == pattern[j])
        {
            kmpNext[i] = kmpNext[j];
        }
        else
        {
            kmpNext[i] = j;
        }
    }
}

#include <iostream>
using namespace std;
#include <string>

void KMP(string p, string t)
{
    int m = p.length();
    int n = t.length();
    if (m>n)
    {
        cerr << "Unsuccessful match!";
    }
    const char* x = p.c_str();
    const char* y = t.c_str();

    int i = 0, j = 0, kmpNext[128];
    preKmp(x, m, kmpNext);

    i = j = 0;
    while (i<n)
    {
        while (j>-1 && x[j] != y[i])
        {
            j = kmpNext[j];
        }
        j++;
        i++;
        if (j >= m)
        {
            cout << "Matching index found at:" << i - j << endl;
            j = kmpNext[j];
        }
    }
}
int main(int argc, char** argv) {

    string p1 = "abcabcad";
    string p2 = "adcadcad";
    string p3 = "ababcaabc";
    string t = "ctcabcabcadtcaabcabcadat";

    cout << "KMP: " << endl;
    KMP(p1, t);

    //  cout<<"KMP: "<<endl;  
    //  KMP(p2, t);  

    //  cout<<"KMP: ";  
    //  KMP(p3, t);  

    return 0;
}
  •  以后kmp算法都按照这样写
void GetNext(char* p, int next[])
{
    int pLen = strlen(p);
    next[0] = -1;
    int k = -1;
    int j = 0;
    while (j < pLen - 1) //j<pLen也没有错
    {
        //p[k]表示前缀,p[j]表示后缀  
        if (k == -1 || p[j] == p[k])
        {
            ++k;
            ++j;
            next[j] = k;  //对于值k,已有p0 p1, ..., pk-1 = pj-k pj-k+1, ..., pj-1,相当于next[j] = k。
        }
        else //当不匹配时,更新新的前缀下标
        {
            k = next[k];
        }
    }
}

// 优化的地方
//当p[j] != s[i] 时,下次匹配必然是p[next[j]] 跟s[i]匹配,如果p[j] = p[next[j]],必然导致后一步匹配失败(因为p[j]已经跟s[i]失配,
//然后你还用跟p[j]等同的值p[next[j]]去跟s[i]匹配,很显然,必然失配),所以不能允许p[j] = p[next[j]]。
//优化过后的next 数组求法  
void GetNextval(char* p, int next[])
{
    int pLen = strlen(p);
    next[0] = -1;
    int k = -1; //前缀
    int j = 0;
    while (j < pLen - 1)  //j<pLen也没有错
    {
        //p[k]表示前缀,p[j]表示后缀    
        if (k == -1 || p[j] == p[k])
        {
            ++j;
            ++k;
            //较之前next数组求法,改动在下面4行  
            if (p[j] != p[k])
                next[j] = k;   //之前只有这一行  
            else
                //因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]  
                next[j] = next[k];
        }
        else
        {
            k = next[k];
        }
    }
}

int KmpSearch(char* s, char* p)
{
    int i = 0;
    int j = 0;
    int sLen = strlen(s);
    int pLen = strlen(p);

    int next[128] = { 0 };
    GetNextval(p,next);

    while (i < sLen && j < pLen)
    {
        //①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++      
        if (j == -1 || s[i] == p[j])
        {
            i++;
            j++;
        }
        else
        {
            //②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]      
            //next[j]即为j所对应的next值        
            j = next[j];
        }
    }
    if (j == pLen)
        return i - j; //此处i-j才是字符串开始匹配的地方
    else
        return -1;
}

 有这样写的,else效果就是j==-1的时候

 while (Text[i] != '\0' && Pattern[j] != '\0')
    {
        if (Text[i] == Pattern[j])
        {
            ++i;// 继续比较后继字符
            ++j;
        }
        else
        {
            index += j - next[j];
            if (next[j] != -1)
                j = next[j];// 模式串向右移动
            else
            {
                j = 0;
                ++i;
            }
        }
    }//while

 

相关文章:

  • log4j详解
  • angularjs data-ng-app 和ng-app的区别
  • 微软发布WF教程及大量示例
  • zabbix3.0.4-agent通过shell脚本获取mysql数据库登陆用户
  • 一个n的flex组件(SpringGraph Flex Component)
  • CString类常用方法(转载)
  • 网站产生流量的几个方法
  • 获取数据库内容二
  • 网页素材
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • Apache Tomcat信息泄露漏洞(CVE-2016-8745)
  • JS部分通用函数
  • Java Integer常量池
  • Internet Explorer快捷键
  • Java中的同步
  • 深入了解以太坊
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • Docker下部署自己的LNMP工作环境
  • HTML5新特性总结
  • input实现文字超出省略号功能
  • JavaScript设计模式之工厂模式
  • js 实现textarea输入字数提示
  • Laravel深入学习6 - 应用体系结构:解耦事件处理器
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • October CMS - 快速入门 9 Images And Galleries
  • React-redux的原理以及使用
  • RxJS: 简单入门
  • SpringCloud集成分布式事务LCN (一)
  • underscore源码剖析之整体架构
  • 动态魔术使用DBMS_SQL
  • 分类模型——Logistics Regression
  • 基于axios的vue插件,让http请求更简单
  • 前端临床手札——文件上传
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 译自由幺半群
  • 数据可视化之下发图实践
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ​MySQL主从复制一致性检测
  • #{}和${}的区别是什么 -- java面试
  • #pragam once 和 #ifndef 预编译头
  • (26)4.7 字符函数和字符串函数
  • (第二周)效能测试
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (一)WLAN定义和基本架构转
  • .“空心村”成因分析及解决对策122344
  • .bat批处理(八):各种形式的变量%0、%i、%%i、var、%var%、!var!的含义和区别
  • .NET 使用 ILRepack 合并多个程序集(替代 ILMerge),避免引入额外的依赖
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args
  • .NET/C# 使窗口永不激活(No Activate 永不获得焦点)
  • .NET国产化改造探索(三)、银河麒麟安装.NET 8环境
  • .NET应用架构设计:原则、模式与实践 目录预览
  • .stream().map与.stream().flatMap的使用