功能

能在线性时间内判断字符串\(A[1~N]\)是否为字符串\(B[1~M]\)的子串,并求出字符串\(A\)在字符串\(B\)中各次出现的位置。

实现

1.对字符串\(A\)进行自我“匹配”,求出一个数组\(next\),其中\(next[i]\)表示“\(A\)中以\(i\)结尾的非前缀子串”与“\(A\)的前缀”能够匹配的最长长度。特别地,当不存在这样的\(j\)时,令\(next[i]=0\).由于\(next\)的对象是非前缀子串,所以\(next[1]=0\);

概念解释:字符串的前缀是指字符串的任意首部。比如字符串“\(abbc\)”的前缀有“\(a\)”,“\(ab\)”,“\(abb\)”,“\(abbc\)”。同样,字符串的任意尾部是字符串的后缀,“\(abbc\)”的后缀有“\(c\)”,“\(bc\)”,“\(bbc\)”,“\(abbc\)”。



\(next[i]=max{j},其中j<i并且A[i-j+1~i]=A[1~j]\)

2.对字符串\(A\)与\(B\)进行匹配,求出一个数组\(f\),其中\(f[i]\)表示“\(B\)中以\(i\)结尾的子串”与“\(A\)的前缀”能够匹配的最长长度。



\(f[i]=max{j},其中j<=i并且B[i-j+1~i]=A[1~j]\)

代码

KMP算法\(next\)数组的求法

1.初始化\(next[1]=j=0\),假设\(next[1~i-1]\)已求出,下面求解\(next[i]\).

2.不断尝试扩展匹配长度\(j\),如果扩展失败(下一个字符不相等),令\(j\)变为\(next[j]\),直至\(j\)为\(0\)(应该从头开始匹配)。

3.如果能够扩展成功,匹配长度\(j\)就增加\(1\).\(next[i]\)的值就是\(j\).

next[1]=0;
for(int i=2,j=0;i<=n;i++)
{
while(j>0&&&a[i]!=a[j+1]) j=next[j];
if(a[i]==a[j+1]) j++;
next[i]=j;
}

KMP算法\(f\)数组的求法

for(int i=1,j=0;i<=m;i++)
{
while(j>0&&(j==n||b[i]!=a[j+1])) j=next[j];
if(b[i]==a[j+1]) j++;
f[i]=j;
//if(f[i]==n),此时就是A在B中的某一次出现
}

例题

例题1:P3375 【模板】KMP字符串匹配

这道题完完全全就是对上述两个代码块的直接使用。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1000010
#define M 1000010
char p[N],s[M];//p模式串 s主串
int nex[N],f[N];
int ans;
using namespace std;
int main()
{
cin>>s+1>>p+1;
int lp=strlen(p+1);
int ls=strlen(s+1);
for(int i=2,j=0;i<=lp;i++)
{
while(j>0&&p[i]!=p[j+1]) j=nex[j];
if(p[i]==p[j+1]) j++;
nex[i]=j;
}
for(int i=1,j=0;i<=ls;i++)
{
while(j&&(j==lp||s[i]!=p[j+1])) j=nex[j];
if(s[i]==p[j+1]) j++;
f[i]=j;
if(f[i]==lp)
{
ans=i-lp+1;
printf("%d\n",ans);
}
}
for(int i=1;i<=lp;i++) printf("%d ",nex[i]);
return 0;
}

例题2







这道题引入了一个新概念:循环同构。

其实,判断两个串是否循环同构也可以利用上述的KMP算法。比如欲判断字符串\(A\)和\(B\)是否循环同构,我们只需要新定义一个字符串\(C\)为重复两遍的\(A\),就是说,若\(A=aab\),则\(C=aabaab\).然后用KMP判断\(C\)中是否有\(B\),若有,则\(A\)和\(B\)循环同构,反之则不循环同构。

具体到这道题,我们可以枚举所有因数,然后按因数分段,再用上述方法判断。一旦出现两段之间非循环同构就break,这样能节省不少时间。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e6+10;
int t,n;
char a[N];
int nex[N],b[N],f[N];
void kmp(int x)
{
nex[1]=0;
for(int i=2,j=0;i<=x;i++)
{
while(j>0&&a[i]!=a[j+1]) j=nex[j];
if(a[i]==a[j+1]) j++;
nex[i]=j;
}
}
bool judge()
{
for(int i=2;i<n;i++)
{
if(n%i==0)
{
int x=i,p=1,w=n/x;
bool kx=1;
kmp(x);
for(int j=1;j<=w-1;j++)
{
for(int k=x*j+1;k<=x*(j+1);k++)
b[k-x*j]=b[k+x-x*j]=a[k];
for(int k=1,w=0;k<=x*2;k++)
{
while(w>0&&(w==x||b[k]!=a[w+1])) w=nex[w];
if(b[k]==a[w+1]) w++;
f[k]=w;
if(f[k]==x)
{
kx=1;
p++;
break;
}
kx=0;
}
if(!kx) break;
}
if(p==w) return 1;
}
}
for(int j=1;j<n;j++)
if(a[j]!=a[j+1])
return 0;
return 1;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%s",&n,a+1);
if(n==1)
{
puts("No");
continue;
}
if(judge()) puts("Yes");
else puts("No");
}
return 0;
}

KMP模式匹配 学习笔记的更多相关文章

  1. KMP字符串模式匹配学习笔记

    KMP算法实验 1.编程计算模式串(子串)的next值.2.利用KMP算法在主串中找到模式串的位置. 参考代码:---------int getNexlVal( char * s,  int j)// ...

  2. [KMP]【学习笔记】

    Oulipo Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 36916   Accepted: 14904 Descript ...

  3. HDU 4333 Revolving Digits [扩展KMP]【学习笔记】

    题意:给一个数字,每一次把它的最后一位拿到最前面,一直那样下去,分别求形成的数字小于,等于和大于原来数的个数. SAM乱搞失败 当然要先变SS了 然后考虑每个后缀前长为n个字符,把它跟S比较就行了 如 ...

  4. 串的应用与kmp算法讲解--学习笔记

    串的应用与kmp算法讲解 1. 写作目的 平时学习总结的学习笔记,方便自己理解加深印象.同时希望可以帮到正在学习这方面知识的同学,可以相互学习.新手上路请多关照,如果问题还请不吝赐教. 2. 串的逻辑 ...

  5. 「学习笔记」字符串基础:Hash,KMP与Trie

    「学习笔记」字符串基础:Hash,KMP与Trie 点击查看目录 目录 「学习笔记」字符串基础:Hash,KMP与Trie Hash 算法 代码 KMP 算法 前置知识:\(\text{Border} ...

  6. 【学习笔记】oracle 比较运算符,逻辑运算符,特殊运算符,判断空值,大小写敏感

    比较运算符:> 大于,< 小于 >= 大于等于,<= 小于等于 = 等于,!=,<>,^= 不等于 逻辑运算符运算的优先顺序:NOT > AND > O ...

  7. 两千行PHP学习笔记

    亲们,如约而至的PHP笔记来啦~绝对干货! 以下为我以前学PHP时做的笔记,时不时的也会添加一些基础知识点进去,有时还翻出来查查. MySQL笔记:一千行MySQL学习笔记http://www.cnb ...

  8. awk 学习笔记

    awk的语法有两种形式 awk [options] 'script' var=value file(s) awk [options] -f scriptfile var=value file(s) 选 ...

  9. PHP学习笔记 - 进阶篇(5)

    PHP学习笔记 - 进阶篇(5) 正则表达式 什么叫正则表达式 正则表达式是对字符串进行操作的一种逻辑公式,就是用一些特定的字符组合成一个规则字符串,称之为正则匹配模式. $p = '/apple/' ...

随机推荐

  1. Docker容器保姆:在centos7.6上利用docker-compose统一管理容器和服务

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_108 众所周知,一个大型的Docker容器组成的微服务应用中,容器的数量是非常巨大的,如果依赖传统的人工配置方式进行维护,对于开发 ...

  2. ViewPlane类定义

    这个类主要是记录了所有跟视图窗口有关的数据,用于显示. 类声明: #pragma once #ifndef __VIEWPLANE_HEADER__ #define __VIEWPLANE_HEADE ...

  3. CSP-J2021 题解

    分糖果 题意 选择L~R中的某个数 , 使得x mod k的结果最大. 思路 分两种情况考虑: 若 L 和 R 对 K 取模后在同一区间,则必然在 x=R 位置取到最大值: 否则 L~R 必然跨越多个 ...

  4. POJ2201 Cartesian Tree (cartesian tree)

    #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> ...

  5. DevOps落地实践点滴和踩坑记录-(2) -聊聊平台建设

    很久没有写文章记录了,上一篇文章像流水账一样,把所见所闻一个个记录下来.这次专门聊聊DevOps平台的建设吧,有些新的体会和思考,希望给正在做这个事情的同学们一些启发吧. DevOps落地实践点滴和踩 ...

  6. Blazor预研与实战

    背景 最近一直在搞一件事,就是熟悉Blazor,后期需要将Blazor真正运用到项目内.前期做了一些调研,包括但不限于 Blazor知识学习 组件库生态预研 与现有SPA框架做比对 与WebForm做 ...

  7. linux tcp 在timewait 状态下的报文处理

    最近处理一个问题,我们nginx服务器作为透明代理,将核心网过来的用户上网请求代理到我们的cache服务器,如果cache服务器没有命中内容,则需要我们 作为客户端往源站请求内容,但用户对此一无所知, ...

  8. java数组---特点,边界

    数组的四个基本特点 1.其长度是确定的.数组一旦被创建,它的大小就是不可以改变的. 2.其元素必须是相同类型,不允许出现混合类型. 3.数组中的元素可以是任何数据类型,包括基本类型和引用类型. 4.数 ...

  9. 搭建docker镜像仓库(一):使用registry搭建本地镜像仓库

    目录 一.系统环境 二.前言 三.使用registry搭建私有镜像仓库 3.1 环境介绍 3.2 k8smaster节点配置镜像仓库 3.3 k8sworker1节点配置从私有仓库上传和拉取镜像 3. ...

  10. Docker实用篇

    Docker实用篇 0.学习目标 1.初识Docker 1.1.什么是Docker 微服务虽然具备各种各样的优势,但服务的拆分通用给部署带来了很大的麻烦. 分布式系统中,依赖的组件非常多,不同组件之间 ...