<更新提示>

<第一次更新>


<正文>

字符串模式匹配

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

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

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;
}

<后记>

最新文章

  1. Accessibility应用之focus篇
  2. Codeforces Round #212 (Div. 2) D. Fools and Foolproof Roads 并查集+优先队列
  3. BZOJ1401 : Hexagon
  4. 层叠水平(stacking level)
  5. ArcGIS中文件共享锁定数据溢出 这个方法不行,建议用gdb,不要用mdb
  6. 使用Chrome DevTools的Timeline分析页面性能
  7. 联系我们_站内信息_站内资讯_网上定制衬衫|衬衫定制|衬衫定做-ChenShanLe衬衫乐
  8. OD调试篇3-小软件破解1
  9. JavaScript 部分对象方法记叙 ing...
  10. C语言--第1次作业
  11. 记一次用express手写博客
  12. Mesos和Docker的集成
  13. Python基础10_函数
  14. fortran中提取字符串中可见字符的索引
  15. 吴裕雄 实战PYTHON编程(8)
  16. PythonQt在windows下的编译
  17. 科学计算三维可视化---TVTK管线与数据加载(用IVTK根据观察管线)
  18. Django Rest Framework(3)-----APIView与Viewsets
  19. Window vista 以上制作自定义证书并为端口配置ssl
  20. Linux下的lds链接脚本详解【转】

热门文章

  1. &lt;转载&gt;Vim的寄存器(复制黏贴要用)
  2. Network Security final project---War Game
  3. PC网站转换成手机版
  4. python学习:输入设置
  5. hashlib模块,shutil,模块 ,,xml 文件解析,configparser,模块,类,什么是类
  6. Java_异常处理
  7. 1.SSM整合_单表的增删改查
  8. 谨以此篇献给DJANGO学习过程中遇到的问题
  9. git 本地同步分支数,删除远程已经删除掉的多余分支
  10. json转义 使用 JavaScriptSerializer 时 需要添加的引用