引入

在计算机中,人类的语言、文字等往往利用字符串进行保存,而字符串的匹配也对人们的生活产生着举足轻重的作用,例如个人信息的匹配、搜索引擎的使用等,为了在庞大的数据中找到我们所需的数据,字符串匹配的效率就决定了解决问题的难易程度。

实现

对于字符串的匹配,最简单且最容易实现的方法就是将模式串与文本串一一比对,观察每一位上的字符是否相等,如果不等则将模式串后移一位,再逐一进行比对,假定模式串的长度为 m m m,文本串的长度为 n n n,则需要进行 ( n − m ) × m \left ( n-m\right)×m (n−m)×m次比较,而一般情况下 n > > m n>>m n>>m,所以朴素的枚举算法的时间复杂度为 O ( n × m ) O(n×m) O(n×m),在模式串和文本串足够大时,该算法的效率将变得低下。

为了解决这个问题,首先要观察是什么原因导致了算法的效率大打折扣。原因在于在每次比对过程中,一旦两个字符串某个位置不匹配,就要重新回溯,往后移动一个位置再从头进行比较。在有些情况下,这种回溯是没有意义的,例如对于文本串abababab和模式串ababc,一般的做法如下:

不难发现,其中第二次比对是没有任何意义的,为了避免这种不必要的回溯,首先要引入前缀和后缀的概念:

对于字符串 s 0 s 1 s 2 . . . s n − 1 s n s_0s_1s_2...s_{n-1}s_n s0​s1​s2​...sn−1​sn​,从 s 0 s_0 s0​开始的任意子串(即不包含 s n s_n sn​)称为该字符串的前缀,类比,以 s n s_n sn​结尾的任意子串(即不包含 s 0 s_0 s0​)称为该字符串的后缀,通过对模式串进行一个预处理,从而避免不必要的回溯,最终使得算法的复杂度降低为 O ( m + n ) O(m+n) O(m+n)。

而KMP算法的核心步骤在于求解 nxt 数组,nxt 数组用于存储模式串中的前缀和后缀相同时的最大长度,其中 i 表示对于模式串其前缀 s 0 s 1 . . . s i − 1 s_0s_1...s_{i-1} s0​s1​...si−1​的长度,nxt[i] 表示该前缀所表示的字符串中前缀和后缀的最大公共长度。
实现代码如下:

void kmp()       //s是从1开始的,便于理解
{nxt[1] = 0;for (int i = 2, k = 0; i <= len; i++){while (k && s[k + 1] != s[i]){k = nxt[k];}if (s[k + 1] == s[i]){k++;}nxt[i] = k;}
}

这段代码用最难以理解的地方就是循环中的while循环,举个例子,当 i = 20 时,模式串的前缀为 ababab…ababab (省略号中不重复,长度为20),此时代码执行完该循环后 k = 6,nxt[20] = 6。进入下一次循环,此时要比对 s[k + 1] 和 s[i],此时 k + 1 的值为 7 ,即比对上一次循环结束后的最长相同前缀的下一位与新加入的字符。

当相同时循环是不执行的,所以只是在上一次循环的基础上最大长度加 1 了而已,也不难理解。重点在于当 s[k + 1] != s[i] 时,循环体到底时如何进行重新匹配前缀和后缀的,考虑这样一个情况,由于当前最后一个字符不等,所以在考虑长度时前缀和后缀的范围应如下图所示:

那么如何来确定这个序列的长度呢,更一般的,假设第 i 次循环所确定的长度为 l l l,那么一定有 s 1 s 2 . . . s l = = s i − l + 1 . . . s i − 1 s i s_1s_2...s_l == s_{i-l+1}...s_{i-1}s_i s1​s2​...sl​==si−l+1​...si−1​si​,那么就有 s 1 s 2 . . . s l − 1 = = s i − l + 1 . . . s i − 2 s i − 1 s_1s_2...s_{l-1} == s_{i-l+1}...s_{i-2}s_{i-1} s1​s2​...sl−1​==si−l+1​...si−2​si−1​,根据这个等式可以发现,这就是前缀与后缀相等的情况,如图所示:

可以发现,新的前缀是原前缀的前缀,新的后缀是原后缀的后缀,而原前缀和原后缀是相等的,那么问题就转化为原前缀的前缀和后缀的最大长度是多少,而这个长度之前已存储在 nxt[l - 1] 中,也就是 nxt[k] 中,进行循环找到最大长度,如果不存在,那么会一直循环下去直到 k = nxt[1],即 k = 0,循环终止,对应也就是首字符和尾字符是否相等,如果不等,那这段序列也就没有相同的前缀和后缀。

例题 CF 535D-Tavas and Malekas



题目大意是给定一个字符串,然后给出 m \;m\; m个位置,将该字符串插入,问能否可以形成一个合法的长度为 n \;n\; n的字符串。分析题目不难得出思路:对于每次插入,只需要判断插入时是否会叠加,如果叠加了重合部分二者是否相等,即对应 KMP 算法的前缀与后缀问题。统计插入结束后还有多少个空位,答案就是 2 6 c n t 26^{cnt} 26cnt,(注意的点都写在注释里了)。
Solution:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int maxn = 1e6 + 5;
inline ll read()
{ll t = 0;char ch = getchar();while (ch < '0' || ch>'9'){ch = getchar();}while (ch >= '0' && ch <= '9'){t = (t << 3) + (t << 1) + (ch ^ 48);ch = getchar();}return t;
}
inline void write(ll t)
{if (t > 9)write(t / 10);putchar(t % 10 + 48);
}
ll n, m, y, num, ans;
ll len, nxt[maxn];
char p[maxn];
inline void kmp()
{nxt[1] = 0;for (int i = 2, j = 0; i <= len; i++){while (j && p[j + 1] != p[i]){j = nxt[j];}if (p[j + 1] == p[i]){j++;}nxt[i] = j;}
}
inline void quickPower()
{ans = 1;ll t = 26;while (num){if (num & 1){ans = (ans * t) % mod;}t = (t * t) % mod;num >>= 1;}ans %= mod;
}
int main()
{n = read();m = read();cin >> p + 1;len = strlen(p + 1);kmp();if (!m)            //如果没有一个插入位置,那么所有n个位置都有26种可能{num = n;}else{ll endPos = 0;      //记录每次插入结束后占据的最后一个位置while (m--){y = read();if (endPos){if (y > endPos)          //新插入位置与上一次插入不重合{num += y - endPos - 1;       //增加二者之间的空格endPos = y + len - 1;      //更新末尾位置}else{ll l = endPos - y + 1;ll h = nxt[len];bool flag = false;while (h)     //坑点,插入位置可能重合但不是最大长度,所以要循环判断每次可能{if (h < l)        //重合部分比最大长度还长,一定不符合要求break;if (h == l)     //二者重合合法,可以插入{flag = true;break;}h = nxt[h];}if (flag)     //重合位置符合要求,更新末尾位置{endPos = y + len - 1;}else{puts("0");return 0;}}}else{//对于第一次插入,无需判断重合,但是插入位置不一定是1号位,所以前面有y - 1个空位num = y - 1;endPos = y + len - 1;}}if (endPos > n)            //当最后一次插入时不足以全部插入,为不符合要求的情况{puts("0");return 0;}if (endPos < n)         //最后一次插入后末尾位置之后还有空位,需要补上{num += n - endPos;}}quickPower();write(ans);putchar(10);return 0;
}

完结撒花!!!

浅析字符串匹配算法——KMP算法(附CF 535D-Tavas and Malekas)相关推荐

  1. 【GO语言实现字符串匹配算法-KMP算法】

    [GO语言实现字符串匹配算法-KMP算法] KMP算法原理说明: KMP算法是一种改进的字符串匹配算法,是有D.E.Knuth,J.H.Morris和V.R.Pratt提出的,所以被称为KMP算法. ...

  2. 字符串匹配算法KMP算法

    数据结构中讲到关于字符串匹配算法时,提到朴素匹配算法,和KMP匹配算法. 朴素匹配算法就是简单的一个一个匹配字符,如果遇到不匹配字符那么就在源字符串中迭代下一个位置一个一个的匹配,这样计算起来会有很多 ...

  3. js实现kmp算法_字符串匹配算法KMP算法

    数据结构中讲到关于字符串匹配算法时,提到朴素匹配算法,和KMP匹配算法. 朴素匹配算法就是简单的一个一个匹配字符,如果遇到不匹配字符那么就在源字符串中迭代下一个位置一个一个的匹配,这样计算起来会有很多 ...

  4. 字符串匹配算法——KMP算法学习

    KMP算法是用来解决字符串的匹配问题的,即在字符串S中寻找字符串P.形式定义:假设存在长度为n的字符数组S[0...n-1],长度为m的字符数组P[0...m-1],是否存在i,使得SiSi+1... ...

  5. [Algorithm] 字符串匹配算法——KMP算法

    1 字符串匹配 字符串匹配是计算机的基本任务之一. 字符串匹配是什么?举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串& ...

  6. 数据结构(C语言版)严蔚敏(字符串的模式匹配算法--KMP算法)

    数据结构(C语言版)严蔚敏(字符串的模式匹配算法–KMP算法) 1.暴力匹配算法 // 暴力匹配算法 int Index2(SString S,SString T) {// S是主串,T是子串int ...

  7. c++ 字符串匹配算法sunday算法

    sunday 算法是一种很高效的字符串匹配算法, 其实现也较kmp算法简单很多 class Sunday {public:const string& needle;int mp[300];Su ...

  8. 字符串匹配算法——暴力算法

    字符串匹配算法--暴力算法,简称BF(BruteForce)BF(Brute\:Force)BF(BruteForce)算法. 基本思想:从主串(str)(str)(str)的第一个字符开始和子串(p ...

  9. 0x15.基本数据结构 — 字符串 (KMP算法(含详细证明)和最小表示法)

    目录 一.KMP模式匹配 1.引理: 2.引理证明: 3.使用优化的算法计算nextnextnext数组: 4.luogu P3375 [模板]KMP字符串匹配 5.UVA1328 Period 6. ...

最新文章

  1. 帧中继和路由协议详解-在帧中继多点子接口上运行EIGRP
  2. Object not found! The requested URL was not found on this server.... 报错解决方案
  3. Java学习之do-while-if语句实操
  4. matlab-绘制分叉与混沌分支图
  5. 模式识别Pattern Recognition和机器学习Machine Learning资源
  6. JavaScript 中遍历对象的属性
  7. Service Agent在Hybris ASM里给一个客户移除了某个优惠券,背后发生了什么
  8. android tag的使用方法,Android:如何使用Html.TagHandler?
  9. java方法示例注释 @_Java注释和真实世界的Spring示例
  10. 【转】C#中枚举类型与静态变量
  11. Android木马病毒com.schemedroid的分析报告
  12. 【过关斩将】如何制作高水平简历-观念篇
  13. delphi xe 之路(14)使用FireMonkeyStyle
  14. 求三点共圆求圆心半径及其推导(三角形外心)
  15. 锋麦4S笔记本英伟达独显驱动安装
  16. 打开conda环境报错:UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte 0x9a in position 317: illegal multibyt
  17. C#往图片上面添加文字
  18. 元气骑士机器人获取方法_元气骑士机器人怎么获得视频(机器人获取方法说明)...
  19. untiy UI的坐标转为屏幕坐标
  20. 嵌入式Linux驱动笔记(三十一)------SYSRQ组合键使用

热门文章

  1. 系统平台pdd七天打造爆款玩法
  2. 简述时钟周期、机器周期、指令周期的概念及三者之间的关系
  3. Jsp页面中双引号问题
  4. 【利用Arcpy快速检索矢量文件和栅格文件并进行统计和删除工作】
  5. 我记:交2中,突破boll线上轨,是否继续买入?
  6. 测试理论--用正交表设计测试用例
  7. 杨强教授演讲视频:数据要素与联邦学习(附文字摘要)
  8. @Modifying注解误用错误
  9. 会议及作用篇--项目管理(二)
  10. 用tsm动作识别训练自己的数据集