<更新提示>

<第一次更新>


<正文>

字符串模式匹配

我们要先了解一下问题是什么。

模式匹配是数据结构中字符串的一种基本运算,给定一个子串,要求在某个字符串中找出与该子串相同的所有子串,这就是模式匹配。

KMP

然后我们来认识一下今天的主角\(KMP\)。

\(KMP\)算法是一种用来解决字符串模式匹配问题的一个经典算法,其能够在线性时间内求出在一个字符串中是否出现了另一个指定字符串,以及出现的位置,出现的次数。

形式化一下问题:给定一个字符串\(A\)和一个字符串\(B\),求\(B\)在\(A\)中的出现次数。

最长前后缀匹配

约定:我们将\(A\)称为主串,\(B\)为匹配串,两个字符串均从下标\(1\)开始储存,对于形如\(substr(l,r)\)认为是字符串中连续一部分。

对于字符串\(B\),\(KMP\)算法中有一个非常重要的关键数组,我们将其称为\(next\)数组,对于\(next_i\),其定义为:字符串\(B\)的\(substr(1,i)\)中,前缀和后缀和最大匹配长度,即\(\max\{next_i\}\),使得$$substr(1,next_i)=substr(i-next_i+1,i)$$

举个例子吧:\(B=ababab\),则\(next_6=4\),因为\(substr(1,4)=substr(3,6)=abab\)。

解决模式匹配问题

假设我们能够在线性时间内求出\(next\)数组,我们可以用如下方式求解该问题。

设\(f_i\)代表主串\(i\)位置的最长匹配长度,枚举\(i\)分别作为主串的指针,并声明一个变量\(j\)作为匹配串的指针,对于\(i\)移动到一个新的位置,我们尝试将\(j\)也向后移动一位,如果匹配,则更新一下当前位置的最优答案\(f_i\)即可。

那么对于不匹配的情况,我们可以用如下方法处理:虽然\(a_i\)与\(b_{j+1}\)不匹配,但我们知道主串的\(substr(i-j,i-1)\)与匹配串的\(substr(1,j)\)是相互匹配的,我们尝试用\(next\)数组来移动指针\(j\)。已知,匹配串中\(substr(j-next_j+1,j)=substr(1,next_j)\),由于匹配串中\(substr(j-next_j+1,j)\)必然也是和主串的一部分匹配的,我们可以直接利用\(next\)数组,使\(j=next_j\),让\(substr(1,next_j)\)移到原来\(substr(j-next_j+1,j)\)的位置,和主串重新进行匹配,继续求解问题。

匹配完成后,更新\(f_i=j\)即可。

\(Code:\)

inline void mate(void)
{
for(int i=1,j=0;i<=n;i++)
{
while(j&&(j==n||a[i]!=b[j+1]))
j=next[j];
if(a[i]==b[j+1])j++;
f[i]=j;
if(f[i]==m)ans++;
}
}

关于正确性,这样必然是正确的,不然将与\(next\)的极大性矛盾。

求解next数组

考虑求解\(next\)数组。如果直接暴力的话,时间复杂度将比求解主问题的时间复杂度还高,我们可以这样考虑,对于原问题,我们求解的是主串和匹配串的最长匹配,而对于\(next\)数组,我们求解的是匹配串的前后缀最长匹配,本质上,这两个问题的一样的,我们可用相同的方法来求解:用匹配串本身 匹配 匹配串,代码几乎是相同的。

\(Code:\)

inline void selfmate(void)
{
next[1]=0;
for(int i=2,j=0;i<=m;i++)
{
while(j&&b[i]!=b[j+1])
j=next[j];
if(b[i]==b[j+1])j++;
next[i]=j;
}
}

模板

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
const int LENTH=1e6+20;
char a[LENTH],b[LENTH];
int next[LENTH],f[LENTH],n,m,ans;
inline void input(void)
{
scanf("%s",a+1);
n=strlen(a+1);
scanf("%s",b+1);
m=strlen(b+1);
}
inline void selfmate(void)
{
next[1]=0;
for(int i=2,j=0;i<=m;i++)
{
while(j&&b[i]!=b[j+1])
j=next[j];
if(b[i]==b[j+1])j++;
next[i]=j;
}
}
inline void mate(void)
{
for(int i=1,j=0;i<=n;i++)
{
while(j&&(j==n||a[i]!=b[j+1]))
j=next[j];
if(a[i]==b[j+1])j++;
f[i]=j;
if(f[i]==m)ans++;
}
}
int main(void)
{
input();
selfmate();
mate();
printf("%d\n",ans);
return 0;
}

接下来会有一道例题。

Censoring(USACO)

Description

Farmer John has purchased a subscription to Good Hooveskeeping magazine for his cows, so they have plenty of material to read while waiting around in the barn during milking sessions. Unfortunately, the latest issue contains a rather inappropriate article on how to cook the perfect steak, which FJ would rather his cows not see (clearly, the magazine is in need of better editorial oversight).

FJ has taken all of the text from the magazine to create the string S of length at most 10^6 characters. From this, he would like to remove occurrences of a substring T to censor the inappropriate content. To do this, Farmer John finds the first occurrence of T in S and deletes it. He then repeats the process again, deleting the first occurrence of T again, continuing until there are no more occurrences of T in S. Note that the deletion of one occurrence might create a new occurrence of T that didn't exist before.

Please help FJ determine the final contents of S after censoring is complete

有一个S串和一个T串,长度均小于1,000,000,设当前串为U串,然后从前往后枚举S串一个字符一个字符往U串里添加,若U串后缀为T,则去掉这个后缀继续流程。

Input Format

The first line will contain S. The second line will contain T. The length of T will be at most that of S, and all characters of S and T will be lower-case alphabet characters (in the range a..z).

Output Format

The string S after all deletions are complete. It is guaranteed that S will not become empty during the deletion process.

Sample Input

whatthemomooofun
moo

Sample Output

whatthefun

解析

题意:就是让你不断地删除匹配串,每一次删除,将主串删除部分的两边合并构成新的主串,最后输出主串。

那么我们就用\(KMP\)算法就可以了,对于删除操作,我们可以直接用栈来模拟,栈中记录主串还存在的字符的下标,对于得到了一个完整的匹配,将栈顶被匹配掉的若干个下标弹出即可。

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+20;
char a[N],b[N];
int n,m,top,next[N],f[N],s[N];
inline void input(void)
{
scanf("%s",a+1);
scanf("%s",b+1);
n=strlen(a+1);
m=strlen(b+1);
}
inline void selfmate(void)
{
next[1]=0;
for(int i=2,j=0;i<=m;i++)
{
while(j&&b[i]!=b[j+1])
j=next[j];
if(b[i]==b[j+1])j++;
next[i]=j;
}
}
inline void mate(void)
{
for(int i=1,j=0;i<=n;i++)
{
while(j&&(j==m||a[i]!=b[j+1]))
j=next[j];
if(a[i]==b[j+1])j++;
f[i]=j;
s[++top]=i;
if(f[i]==m)
{
top-=m;
j=f[s[top]];
}
}
}
int main(void)
{
input();
selfmate();
mate();
for(int i=1;i<=top;i++)
printf("%c",a[s[i]]);
return 0;
}

<后记>

『字符串模式匹配 KMP』的更多相关文章

  1. 字符串模式匹配KMP算法

    一篇不错的博客:http://www.cnblogs.com/dolphin0520/archive/2011/08/24/2151846.html KMP字符串模式匹配通俗点说就是一种在一个字符串中 ...

  2. 字符串模式匹配——KMP算法

    KMP算法匹配字符串 朴素匹配算法   字符串的模式匹配的方法刚开始是朴素匹配算法,也就是经常说的暴力匹配,说白了就是用子串去和父串一个一个匹配,从父串的第一个字符开始匹配,如果匹配到某一个失配了,就 ...

  3. 数据结构4.3_字符串模式匹配——KMP算法详解

    next数组表示字符串前后缀匹配的最大长度.是KMP算法的精髓所在.可以起到决定模式字符串右移多少长度以达到跳跃式匹配的高效模式. 以下是对next数组的解释: 如何求next数组: 相关链接:按顺序 ...

  4. KMP字符串模式匹配详解(转)

    来自CSDN     A_B_C_ABC 网友 KMP字符串模式匹配通俗点说就是一种在一个字符串中定位另一个串的高效算法.简单匹配算法的时间复杂度为O(m*n);KMP匹配算法.可以证明它的时间复杂度 ...

  5. 『Python基础-4』字符串

    # 『Python基础-4』字符串 目录 1.什么是字符串 2.修改字符串 2.1 修改字符串大小 2.2 合并(拼接)字符串 2.3 使用乘号'*'来实现字符串的叠加效果. 2.4 在字符串中添加空 ...

  6. KMP字符串模式匹配详解(zz)

    刚看到位兄弟也贴了份KMP算法说明,但本人觉得说的不是很详细,当初我在看这个算法的时候也看的头晕昏昏的,我贴的这份也是网上找的.且听详细分解: KMP字符串模式匹配详解 来自CSDN     A_B_ ...

  7. KMP字符串模式匹配详解

    KMP字符串模式匹配详解 http://www.cppblog.com/oosky/archive/2006/07/06/9486.html

  8. 2017-2018-2 20155303『网络对抗技术』Exp9:Web安全基础

    2017-2018-2 『网络对抗技术』Exp9:Web安全基础 --------CONTENTS-------- 一.基础问题回答 1.SQL注入攻击原理,如何防御? 2.XSS攻击的原理,如何防御 ...

  9. 字符串模式匹配sunday算法

    文字部分转自:http://www.cnblogs.com/mr-ghostaqi/p/4285868.html 代码是我自己写的 今天在做LeetCode的时候,碰到一个写字符串匹配的题目: htt ...

随机推荐

  1. Java中超大文件读写

    如果文件过大不能一次加载,就可以利用缓冲区: File file = new File(filepath); BufferedInputStream fis = new BufferedInputSt ...

  2. session的几种模式

    https://www.cnblogs.com/panchunting/archive/2012/06/25/ASPNET_Session_Mode.html token的生成和应用 session和 ...

  3. python:PATH、PYTHONPATH 和 sys.path 的区别

    python:PATH.PYTHONPATH 和 sys.path 的区别 共同点 所有在它们的路径里面的 moduel 都可以被 import PATH 在 PATH 中的一些命令,例如 *.exe ...

  4. 关闭或启动linux防火墙后,docker启动容器报错

    # docker启动报错   # 解决办法:重建docker0网络恢复   #按照进程名杀死docker进程 [root@localhost mysqlconf]# pkill docker #清空防 ...

  5. 学习笔记-----《Pattern Recognition and Machine Learning》Christopher M. Bishop

    Preface 模式识别这个词,以前一直不懂是什么意思,直到今年初,才开始打算读这本广为推荐的书,初步了解到,它的大致意思是从数据中发现特征,规律,属于机器学习的一个分支. 在前言中,阐述了什么是模式 ...

  6. TypeScript-封装

    class People { private _name: string; age: number; print() { return this._name + ":" + thi ...

  7. WinForm的.Designer.cs代码内抛反射异常

    今天在项目内一个Winform增加控件后,无法打开,抛如下异常. System.Reflection.TargetInvocationException: Exception has been thr ...

  8. 2、java基础

    1.注释  -----> 注释不会出现在字节码文件中.即Java编译器编译时会跳过注释语句. // 单行注释 ,注释内容从//到本行末尾 /*  */ 多行注释,/* */ 注释不能嵌套 /** ...

  9. python学习:购物车程序

    购物车程序 product_list = [ ('mac',9000), ('kindle',800), ('tesla',900000), ('python book',105), ('bike', ...

  10. 方便快捷的求导求积分解方程在线工具sage介绍

      有时候我们需要进行一些复杂的数学计算,比如求导, 求积分,解方程,还是用abcd字母代表变量的方程等,这就需要进行复杂的数学运算还需要具备良好的数学基础.不过现在有一个非常方便的在线工具,只需要几 ...