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

kmp の 笔记

预备知识

1.前后缀

s[i] 表示字符串 S 第 i 个位置的字母 ,下标从 1 开始

Prefix_s [i] 表示 S 的长度为 i 的前缀
Suffix_s [i] 表示 S 的长度为 i 的后缀

2. border

如果一个字符串的同长度前缀和后缀完全相同
称为 字符串的一个 border

S = bbabbab

b 和 bbab 为 这个字符串的 两个 border

3.border 的性质(很重要)

1.考虑 prefix[i] 的所有 border 去掉最后一个字母 就会变成 prefix[i-1] 的一个 border(求nex数组的原理)
2.border具有传递性 , S的border 的 border 一定是 S 的border(求nex数组的原理)
S 的最大border 在串里最少出现 两次 ,S最大border 的子border 在串中最少出现四次 ,依次 × 2
3.p 是 S 的 周期 那么 |S| - p 一定是 S 的 border

4. 循环周期

S = bbabbab

循环周期

  1. bba
  2. bbabba
  3. bbabbab

循环节

满足 p ∣ ∣ s ∣ p | |s| p∣∣s时,把 p 称作 S 的一个循环节

对于 kmp 的理解

1.求nex数组

int nex[10001];
void GetNext(string ss)
{
	int len = ss.size();
	
	nex[1] = 0;
	
	for(int i=2;i<=len;i++){
		nex[i] = nex[i-1];
		
		//先继承 , 继承多少决定是否能往下循环
		
		while(nex[i] && ss[i-1] != ss[nex[i]]) nex[i] = nex[nex[i]];
		//在 pre[i] 的所有 border 中找到一个最大子 border
		
		// nex[i] 的子 border 是 nex[nex[i]] 不一定是 nex[i-1] 所以递归找到的第一个 一定是最大子 border 因为 子border的长度一定比本身短
		
		nex[i] += (ss[i-1] == ss[nex[i]]); 
		// ss[i-1] 后缀新加字母
		// ss[nex[i]] 是前缀新加字母 
	}	
}
// bbabbab
/*
考虑 prefix[i] 的所有 border 去掉最后一个字母 就会变成 prefix[i-1] 的一个 border
从 prefix[i-1] 推 prefix[i] 就是对 prefix[i-1] 所有 border 进行加位操作
*/

KMP字符串匹配

#include<bits/stdc++.h>
using namespace std;
         
#define fi first
#define se second  
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
typedef unsigned long long ull;
typedef long long ll;
const int N = 1e6+10;
const int NN = 1e5+100;
const int pp = 998244353;
typedef pair<int,int>PII;
const int inf = 2147483647;
 
int nex[N],k,ans,n;
string s1,s2;
int len1,len2;
 
void init(string s){
	
	n = s.size();	
	
    for(int i=2;i<=n;i++){
        nex[i] = nex[i-1];
        while(nex[i] && s[nex[i]] != s[i-1]) nex[i] = nex[nex[i]];
        nex[i] += (s[i-1] == s[nex[i]]);
    }
}
// bbabbab

void kmp(string s1,string s2){
	
	for(int i=1,j=0;i<=len1;i++){
		
		while(j && s1[i-1] != s2[j]) j = nex[j];
		
		// j 保证 border 链非空
		
		if(s1[i-1] == s2[j]) j++;
		
//		cout << j << "\n" ;
		
		if(j == len2) cout << i - len2 + 1 << "\n" , j = nex[j] ;
	}
}
 
int main(){
     
    IOS;
    
    cin >> s1 >> s2 ;
    
    len1 = s1.size();
	len2 = s2.size();
    
    init(s2);
     
    kmp(s1,s2); 
    
    
	for(int i=1;i<=len2;i++){
		cout << nex[i] << " " ;
	}
     
     
         
    return 0;
}

相关文章:

  • 最新网站证书提示风险的原因和几个解决方法
  • lambda表达式(C++11)
  • java计算机毕业设计图书共享系统源代码+数据库+系统+lw文档
  • 用Python生成Hilbert矩阵
  • 云计算与云原生
  • JBoss安装并部署war包
  • VGG论文
  • Tableau1——条形图和直方图
  • 微信小程序新手向——界面布局
  • [ vulhub漏洞复现篇 ] Django SQL注入漏洞复现 CVE-2021-35042
  • 宠物狗网页制作作业 保护动物网页设计模板 简单学生网页设计 静态HTML CSS网站制作成品
  • java毕业设计——基于java+Applet+access的综合测评系统设计与实现(毕业论文+程序源码)——综合测评系统
  • 索引的数据结构(2)
  • 利用霍夫变换进行车道线检测
  • 公众号题库系统-查题公众号必备
  • Android单元测试 - 几个重要问题
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • flask接收请求并推入栈
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • Java程序员幽默爆笑锦集
  • JS专题之继承
  • Vue UI框架库开发介绍
  • webgl (原生)基础入门指南【一】
  • 阿里云前端周刊 - 第 26 期
  • 编写高质量JavaScript代码之并发
  • 大整数乘法-表格法
  • 技术胖1-4季视频复习— (看视频笔记)
  • 如何使用 OAuth 2.0 将 LinkedIn 集成入 iOS 应用
  • 数组大概知多少
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 源码之下无秘密 ── 做最好的 Netty 源码分析教程
  • postgresql行列转换函数
  • python最赚钱的4个方向,你最心动的是哪个?
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • (42)STM32——LCD显示屏实验笔记
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (二)换源+apt-get基础配置+搜狗拼音
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (原)本想说脏话,奈何已放下
  • (转)EOS中账户、钱包和密钥的关系
  • .Mobi域名介绍
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .NET Core中的去虚
  • .NET 药厂业务系统 CPU爆高分析
  • .NET建议使用的大小写命名原则
  • @ 代码随想录算法训练营第8周(C语言)|Day57(动态规划)
  • @hook扩展分析
  • [BZOJ2850]巧克力王国
  • [bzoj4010][HNOI2015]菜肴制作_贪心_拓扑排序
  • [C#]C# winform部署yolov8目标检测的openvino模型
  • [C语言]——C语言常见概念(1)
  • [DEBUG] spring boot-如何处理链接中的空格等特殊字符
  • [Eclipse] 详细设置护眼背景色和字体颜色并导出