浅谈 KMP

简介: KMP算法是一种高效的字符串匹配算法,由Knuth、Morris和Pratt提出。它通过预处理模式串构建next数组,利用匹配失败时的信息减少重复比较,从而提升匹配效率。其时间复杂度为O(m+n),适用于大规模文本匹配场景。

何为 KMP:

KMP算法是一种改进的字符串匹配算法,由 D.E.Knuth,J.H.Morris 和 V.R.Pratt 提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称 KMP 算法)。KMP 算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个 $\operatorname{next()}$ 函数实现,函数本身包含了模式串的局部匹配信息。KMP 算法的时间复杂度 $\mathcal{O}(m+n)$。

实现方法:

我们可以发现,KMP 的题目哈希似乎也可以做出来。但是我们可以发现,哈希其实有很多无用功。我们看下图:\

可以发现,在 c 的时候失配(失去匹配)了,但是前面的都符合。如果是哈希,就会像这样匹配:\

很明显,第 $2,4$ 次是无用功,而 KMP 就是为了避免这种无用功。具体是怎么避免的,请往下看。\

我们可以看到上图标红的区域其实是相同的,而第一次符合的就可以与自己本身所对应的先提前比较,避免浪费时间来处理整个的。\

那么怎么处理呢?我们需要用一个 $nxt$ 数组,而我们需要判断的就是,在一个固定的子串中,最长前后缀长度就是可以避免的。\

比如说下图:\aly.tobigirl.com22

我们可以看到这一个字符串 $S$ 所对应的 $nxt$ 数组的数值其实是逐步递增的,那么我们就能得到一个 $\operatorname{getnext}$ 函数,如下:

代码语言:cpp

代码运行次数:0

运行

AI代码解释

void Get_Next(string x){
  int i=0,j=-1;
  nxt[0]=-1;
  while(i<len2){
    if(j==-1||x[i]==x[j]){
      i++;
      j++;
      nxt[i]=j;
    }
    else{
      j=nxt[j];
    }
  }
}

看完后发现并不难,接下来再附上 KMP 那一段代码,主要是运用 $nxt$ 数组,其实想一想并不难。

代码语言:cpp

代码运行次数:0

运行

AI代码解释

void kmp(string x,string y){
  gn(y);
  int i=0,j=0,ans=0;
  while(i<len1){
    if(j==-1||x[i]==y[j]){
      i++;
      j++;
    }
    else{
      j=nxt[j];
    }
    if(j==len2){
      ans++;
      j=nxt[j];
    }
  }
}

例题:

本期例题较少,以理解为主。题单

P3375 【模板】KMP:aly.miithub.com11

模板题,没得说。

代码语言:cpp

代码运行次数:0

运行

AI代码解释

#include<bits/stdc++.h>
using namespace std;
int len1,len2,nxt[1000005];
void gn(string x){
  nxt[0]=-1;
  int i=0,j=-1;
  while(i<len2){
    if(j==-1||x[i]==x[j]){
      i++;
      j++;
      nxt[i]=j;
    }
    else{
      j=nxt[j];
    }
  }
}
void kmp(string x,string y){
  gn(y);
  int i=0,j=0;
  while(i<len1){
    if(j==-1||x[i]==y[j]){
      i++;
      j++;
    }
    else{
      j=nxt[j];
    }
    if(j==len2){
      cout<<i-j+1<<"\n";
      j=nxt[j];
    }
  }
}
int main(){
  string x,y;
  cin>>x>>y;
  len1=x.size();
  len2=y.size();
  kmp(x,y);
  for(int i=1;i<=len2;i++){
    cout<<nxt[i]<<" ";
  }
  return 0;
}

P4824 USACO15FEB Censoring S:

用栈来维护 KMP 的操作,对于后退时不一定要全退,注意就好了。

代码语言:cpp

代码运行次数:0

运行

AI代码解释

#include<bits/stdc++.h>
using namespace std;
int len1,len2,nxt[1000005];
int st[1000005],top,ss[1000005];
void gn(string x){
  nxt[0]=-1;
  int i=0,j=-1;
  while(i<len2){
    if(j==-1||x[i]==x[j]){
      i++;
      j++;
      nxt[i]=j;
    }
    else{
      j=nxt[j];
    }
  }
}
void kmp(string x,string y){
  gn(y);
  int i=0,j=0;
  while(i<len1){
    if(j==-1||x[i]==y[j]){
      st[++top]=i;
      ss[i]=j;
      i++;
      j++;
    }
    else{
      j=nxt[j];
    }
    if(j==len2-1){
      top-=len2;
      j=ss[st[top]];
    }
  }
}
int main(){
  string x,y;
  cin>>x>>y;
  len1=x.size();
  len2=y.size();
  kmp(x,y);
  for(int i=1;i<=top;i++){
    cout<<x[st[i]];
  }
  return 0;
}

P3435 POI 2006 OKR-Periods of Words:

留做练习。

相关文章
|
8月前
|
人工智能 自然语言处理 运维
【新模型速递】PAI-Model Gallery云上一键部署Qwen3-Coder模型
Qwen3-Coder 是通义千问最新开源的 AI 编程大模型正式开源,拥有卓越的代码和 Agent 能力,在多领域取得了开源模型的 SOTA 效果。PAI 已支持最强版本 Qwen3-Coder-480B-A35B-Instruct 的云上一键部署。
|
9月前
|
Dart 前端开发 JavaScript
【HarmonyOS 5】鸿蒙跨平台开发方案详解 (三)
学习曲线、工具支持、代码复用率、热重载能力
582 0
|
7月前
|
人工智能 缓存 安全
你还是没有理解CAS
在高并发场景下,使用 `count++` 统计商品浏览次数可能导致计数丢失。本文介绍了如何使用 CAS(Compare and Swap)实现无锁的原子操作来解决该问题。CAS 通过比较内存值与期望值,确保更新操作的原子性,避免了线程竞争带来的数据错误。文章详细解析了 CAS 的工作机制、优势与局限性,并结合 Java 示例展示了其底层实现与实际应用,如高性能计数器、无锁栈和缓存更新策略。此外,还探讨了 CAS 可能引发的 ABA 问题及其解决方案,如版本号机制。最后,通过性能对比分析,帮助开发者根据场景合理选择并发控制方式。
179 0
|
7月前
|
人工智能 边缘计算 自然语言处理
普通电脑也能跑AI:10个8GB内存的小型本地LLM模型推荐
随着模型量化技术的发展,大语言模型(LLM)如今可在低配置设备上高效运行。本文介绍本地部署LLM的核心技术、主流工具及十大轻量级模型,探讨如何在8GB内存环境下实现高性能AI推理,涵盖数据隐私、成本控制与部署灵活性等优势。
6014 0
普通电脑也能跑AI:10个8GB内存的小型本地LLM模型推荐
|
7月前
|
存储 人工智能 Shell
Lua与C语言接口编程实战指南:打造高性能、灵活的程序
本文深入介绍了 Lua 与 C 语言的交互机制,重点分析了 Lua 作为胶水语言在嵌入式系统、游戏开发(如 Skynet、OpenResty)中的应用。内容涵盖 Lua 环境搭建、虚拟栈管理、C 与 Lua 的相互调用、闭包、Userdata 和注册表的使用等核心技术,并结合代码示例讲解了如何在实际项目中实现 Lua 与 C 的高效交互,适合希望掌握 Lua 扩展与嵌入开发的工程师参考学习。
895 0
|
7月前
|
人工智能 网络协议 Java
skynet对半关闭状态的支持
TCP四次挥手中,半关闭状态是否需要处理取决于具体应用场景。半关闭是指连接的一端关闭读或写通道,另一端仍可继续传输数据。在游戏服务器等场景中,需关注半关闭以确保数据完整发送。Java的Netty和Skynet框架对此有解决方案。Skynet通过reactor模型和epoll机制实现半关闭支持,确保在关闭写端前发送完剩余数据。测试表明,正确处理半关闭可避免数据丢失,提升连接关闭的可靠性。
128 0
|
7月前
|
人工智能 编译器 C语言
C语言模拟面向对象三大特性与C++实现对比
C语言通过结构体和函数指针模拟面向对象特性,实现封装、继承和多态,而C++则通过原生语法支持。两者在实现原理上有相似之处,但C++在语法、编译期检查和内存管理方面更具优势,提高了代码的安全性和开发效率。
121 0
|
机器学习/深度学习 自然语言处理 算法
生成式 AI 大语言模型(LLMs)核心算法及源码解析:预训练篇
生成式 AI 大语言模型(LLMs)核心算法及源码解析:预训练篇
3458 1

热门文章

最新文章