<更新提示>

<第一次更新>


<正文>

字符串模式匹配

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

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

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. go语言实现https的简单get和post请求

    package main import ( "crypto/tls" "fmt" "io" "io/ioutil" &q ...

  2. Cashier Employment 差分约束

    题意:有一个超市需要一些出纳员,已给出这个超市在各个时间段(0-1,1-2,2-3...共24个时间段)至少需要的出纳员数目,现在前来应聘有n个人,每个人都有一个固定的开始工作的时间,这也意味着从这个 ...

  3. 【java】-- 线程池原理分析

    1.为什么要学习使用多线程? 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担. 线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致 ...

  4. [CF1093G]Multidimensional Queries

    [CF1093G]Multidimensional Queries 题目大意: \(k(k\le5)\)维空间中有\(n(n\le2\times10^5)\)个点.\(m\)次操作,操作包含一下两种: ...

  5. vue自定义键盘事件

    //自定义全局按键修饰符 Vue.config.keyCodes.f2 = 13;//enter为13此时F2==ENTER 调用;@keyup.f2='addData()'

  6. PHP使用CURL抓取网页

    CURL是一个非常强大的开源库,支持很多协议,包括HTTP.FTP.TELNET等,我们使用它来发送HTTP请求.它给我 们带来的好处是可以通过灵活的选项设置不同的HTTP协议参数,并且支持HTTPS ...

  7. Spring AOP Capabilities ang goals

    Spring AOP 是用纯JAVA 实现的. 不需借助JAVA代码在编译处理阶段来实现. Spring 是在运行期实现的.AOP的实现可以在编译,加载,运行三个阶段来实现:Spring AOP 也不 ...

  8. libevent入门介绍

    libevent是之前看到的一个别人推荐的清凉级网络库,我就想了解一下它.今天下载到了一个人写的剖析系列,从结构和源码方面进行了简要分析.只是这个分析文章是2010年的,有点过时了(跟现在的libev ...

  9. 微信小程序开发----微信开发者工具使用

    新建项目选择小程序项目,选择代码存放的硬盘路径,填入刚刚申请到的小程序的 AppID,给你的项目起一个好听的名字,最后,勾选 "创建 QuickStart 项目" (注意: 你要选 ...

  10. node.js 递归复制文件夹(附带文件过滤功能)

    1.简介: 很简单,写了一个node操作文件的小脚本,主要实现对目标文件夹中内容的复制.还顺带一个按照文件夹或者文件名过滤的功能. 2.应用场景 适合基于 node 环境的项目,项目打包的时候,配合 ...