『字符串模式匹配 KMP』
<更新提示>
<第一次更新>
<正文>
字符串模式匹配
我们要先了解一下问题是什么。
模式匹配是数据结构中字符串的一种基本运算,给定一个子串,要求在某个字符串中找出与该子串相同的所有子串,这就是模式匹配。
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』的更多相关文章
- 字符串模式匹配KMP算法
一篇不错的博客:http://www.cnblogs.com/dolphin0520/archive/2011/08/24/2151846.html KMP字符串模式匹配通俗点说就是一种在一个字符串中 ...
- 字符串模式匹配——KMP算法
KMP算法匹配字符串 朴素匹配算法 字符串的模式匹配的方法刚开始是朴素匹配算法,也就是经常说的暴力匹配,说白了就是用子串去和父串一个一个匹配,从父串的第一个字符开始匹配,如果匹配到某一个失配了,就 ...
- 数据结构4.3_字符串模式匹配——KMP算法详解
next数组表示字符串前后缀匹配的最大长度.是KMP算法的精髓所在.可以起到决定模式字符串右移多少长度以达到跳跃式匹配的高效模式. 以下是对next数组的解释: 如何求next数组: 相关链接:按顺序 ...
- KMP字符串模式匹配详解(转)
来自CSDN A_B_C_ABC 网友 KMP字符串模式匹配通俗点说就是一种在一个字符串中定位另一个串的高效算法.简单匹配算法的时间复杂度为O(m*n);KMP匹配算法.可以证明它的时间复杂度 ...
- 『Python基础-4』字符串
# 『Python基础-4』字符串 目录 1.什么是字符串 2.修改字符串 2.1 修改字符串大小 2.2 合并(拼接)字符串 2.3 使用乘号'*'来实现字符串的叠加效果. 2.4 在字符串中添加空 ...
- KMP字符串模式匹配详解(zz)
刚看到位兄弟也贴了份KMP算法说明,但本人觉得说的不是很详细,当初我在看这个算法的时候也看的头晕昏昏的,我贴的这份也是网上找的.且听详细分解: KMP字符串模式匹配详解 来自CSDN A_B_ ...
- KMP字符串模式匹配详解
KMP字符串模式匹配详解 http://www.cppblog.com/oosky/archive/2006/07/06/9486.html
- 2017-2018-2 20155303『网络对抗技术』Exp9:Web安全基础
2017-2018-2 『网络对抗技术』Exp9:Web安全基础 --------CONTENTS-------- 一.基础问题回答 1.SQL注入攻击原理,如何防御? 2.XSS攻击的原理,如何防御 ...
- 字符串模式匹配sunday算法
文字部分转自:http://www.cnblogs.com/mr-ghostaqi/p/4285868.html 代码是我自己写的 今天在做LeetCode的时候,碰到一个写字符串匹配的题目: htt ...
随机推荐
- linux学习之命令的排列、替换和别名--2019-04-23
1.命令的排列 1)使用“;” 使用“;”命令时,不管命令1是否出错,接下来都执行命令2. 2)使用“&&” 使用“&&”命令时,只有命令1正确运行,接下来才会执行命令 ...
- [Union]C++中Union学习笔记
C++ union结构式一种特殊的类.它能够包含访问权限.成员变量.成员函数(可以包含构造函数和析构函数).它不能包含虚函数和静态数据变量.它也不能被用作其他类的基类,它本身也不能有从某个基类派生而来 ...
- 爬虫之scrapy-redis
redis分布式部署 scrapy框架是否可以自己实现分布式? 不可以原因有两点 其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器无法分配start_urls列表中的u ...
- Android常规布局方式和方法
一.关于给控件添加ID属性 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xm ...
- java用jsoup解析HTML
步骤 1获取document对象 //方法一 Document doc = Jsoup.connect(网址).get() //方法二 Document doc = Jsoup.parse(html字 ...
- asp.net core 下载文件,上传excel文件
下载文件: 代码: 后端代码: public IActionResult DownloadFile() { var FilePath = @"./files/deparment.xlsx&q ...
- Tomcat问题
1 Tomcat控制台中文乱码 打开tomcat/conf/logging.properties 找到java.util.logging.ConsoleHandler.encoding = UTF-8 ...
- swust oj 1012
哈希表(链地址法处理冲突) 1000(ms) 10000(kb) 2542 / 6517 采用除留余数法(H(key)=key %n)建立长度为n的哈希表,处理冲突用链地址法.建立链表的时候采用尾插法 ...
- java中List<Map<String, Object>>关于null的判断
List<Map<String, Object>> selectTmFileInfo = fileInfoService.selectTmFileInfoByToken(cTo ...
- js面向对象和php面向对象的区别
---恢复内容开始--- js的面向对象 1.类 具体相同的特征的一些对象的集合. 2.对象 具体到某一个失误了都可以叫做对象. 3.类 通过function 定义类 所以在js里类的本质是函数, ...