【字符串】KMP

Algorithm

Task

给定一个文本串 \(S\) 和一个模式串 \(T\),求 \(T\) 在 \(S\) 中出现的所有位置。

Limitations

要求时空复杂度均为线性。

Solution

回头重新学一遍看毛片 KMP 算法。

设 \(X\) 是一个字符串,则以下表述中,\(X_u\) 代表 \(X\) 的第 \(u\) 个字符,\(X_{u \sim v}\) 代表 \(X\) 的从 \(u\) 起到 \(v\) 结束的字串。

首先定义一个字符串的公共前后缀为这个字符串的一个 \(border\),最长公共前后缀称为最长 \(border\)。特别的,不认为字符串本身是自身的 \(border\)。

性质:字符串 \(S\) 的 \(border\) 的 \(border\) 一定是 \(S\) 的 \(border\),正确性显然。因此不断地跳最长 \(border\) 可以遍历字符串的所有 \(border\)

例如,对于字符串 \(abaab\) 来说,其唯一的 \(border\) 是 \(ab\)。

暴力匹配两个字符串,时间复杂度为 \(O(|S||T|)\),考虑优化这个算法。

假设当前匹配时 \(S\) 扫描到了第 \(i\) 位, \(T\) 扫描到了第 \(j\) 位,且 \(S\) 从 \(i\) 向前 \(j\) 位组成的字符串与 \(T\) 的前 \(j\) 位相同,而 \(S_{i + 1} \neq T_{j+1}\),我们称为发生了失配。

考虑失配时,指针 \(i\) 不变,只有将指针 \(j\) 前移,才可能令下一位成功匹配。由于 \(i\) 不变,所以下一个可能发生匹配的字符串一定是 \(T_{1 \sim j}\) 的某个前缀 \(T_{1 \sim k}\) 满足

\[T_{1 \sim k} = S_{i - k + 1 \sim i}\]

其中由于 \(T_{1 \sim k}\) 是 \(T_{1 \sim j}\) 的字串,一定有 \(k < j\)。由于 \(S_{1 \sim i}\) 的后 \(j\) 位与 \(T\) 的前 \(j\) 位匹配,又有 \(k < j\),因此 \(T_{1 \sim j}\) 的后 \(k\) 位一定与 \(S_{1 \sim i}\) 的后 \(k\) 位即 \(S_{i - k + 1 \sim i}\) 匹配。得出

\[T_{j - k + 1 \sim j} = S_{i - k + 1 \sim i}\]

上面两个式子等量代换得到

\[T_{1 \sim k} = T_{j - k + 1 \sim j}\]

由 \(border\) 的定义,我们发现 \(T_{1 \sim k}\) 一定是 \(T_{1 \sim j}\) 的 \(border\)。根据 \(border\) 的性质,我们只需要不断的跳 \(T_{1 \sim j}\) 的最长 \(border\) 即可找到一个最长的可以与 \(S_{1 \sim i}\) 的后几位匹配的字串。因此问题转化为了如何求一个字符串 \(T\) 的所有前缀的最长 \(border\)。

显然 \(border_1 = 0\)。从第 \(2\) 位开始,我们发现问题等价于用 \(T\)(模式串) 的一个前缀去匹配 \(T_{1 \sim i}\) (文本串)的一个后缀,求这个后缀最长是多少,而这个问题的解决方法与上面那个问题的方法 完 全 一 致,都是不断跳 \(border\) 即可。在 \(i\) 与 \(j\) 成功匹配时,记录 \(border_i = j\)。而在这个问题中,由于 \(j\) 恒小于 \(i\),正向扫描 \(i\) 时,所用到的 \(border\) 值都已经被计算出,因此可以得出正确的结果。

考虑时间复杂度:一个显然的事实是每次跳 \(border\) 模式串指针 \(j\) 都会至少减少 \(1\),而当且仅当第 \(S_{i+1}\) 与第 \(T_{j+1}\) 匹配时,\(j\) 才会自增,因此 \(j\) 仅增加了 \(O(|S|)\),因此 \(j\) 只可能减少 \(O(|S|)\) 次,所以跳 \(border\) 的总次数不超过 \(O(|S|)\),而扫描整个文本串需要 \(O(|S|)\) 的时间,因此总时间复杂度 \(O(|S|)\)。

Example

P3375 【模板】KMP字符串匹配

Description

给定一个文本串 \(S\) 和一个模式串 \(T\),求 \(T\) 在 \(S\) 中出现的所有位置,同时要求输出 \(T\) 的每个前缀的 \(border\) 长度。

Limitations

字符串长度不超过 \(10^6\)

Solution

板板题

Code

#include <cstdio>
#include <cstring>

const int maxn = 1000006;

char S[maxn], T[maxn];
int nxt[maxn];

void KMP(char *A, char *B, int x, int y, const bool pt);

int main() {
  freopen("1.in", "r", stdin);
  scanf("%s\n%s", S + 1, T + 1);
  int x = strlen(S + 1), y = strlen(T + 1);
  KMP(T, T, y, y, false); KMP(S, T, x, y, true);
  for (int i = 1; i <= y; ++i) {
    qw(nxt[i], i == y ? '\n' : ' ', true);
  }
  return 0;
}

void KMP(char *A, char *B, int x, int y, const bool pt) {
  for (int j = 0, i = pt ? 1 : 2; i <= x; ++i) {
    while (j && (B[j+1] != A[i])) j = nxt[j];
    if (B[j+1] == A[i]) ++j;
    if (!pt) nxt[i] = j;
    if (j == y) {
      qw(i - y + 1, '\n', true);
    }
  }
}

原文地址:https://www.cnblogs.com/yifusuyi/p/11456710.html

时间: 2024-10-30 09:47:10

【字符串】KMP的相关文章

HDU 2594 Simpsons’ Hidden Talents (字符串-KMP)

Simpsons' Hidden Talents Problem Description Homer: Marge, I just figured out a way to discover some of the talents we weren't aware we had. Marge: Yeah, what is it? Homer: Take me for example. I want to find out if I have a talent in politics, OK? M

hdu 5510 Bazinga(字符串kmp)

Bazinga Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 2287    Accepted Submission(s): 713 Problem Description Ladies and gentlemen, please sit up straight.Don't tilt your head. I'm serious.For

Chapter 4. 字符串 KMP算法

Chapter 4. 字符串 KMP算法 Sylvia's I. 讲的比较好的博客1,博客2 表问窝,在窝翻遍全网的博客后,窝已经处于混乱状态-- Sylvia's II. 窝是贴代码的小能手-- //前方高能请注意//窝并不知道窝在说什么--#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> using namespa

字符串kmp算法详解

之前要研究aho-corasick算法 拖了好久  感觉自己博客要开始了!! aho-corasick算法依赖2元素: 1.Trie树解析,1个月前就已经写过博客分析过了. 2.KMP算法 此文重点介绍字符串KMP算法: 一开始说说普通模式算法("BF"算法)思路:模式串从主串的第一个字符开始匹配,每匹配失败,主串中记录匹配进度的指针 i 都要进行 i-j+1 的回退操作(这个过程称为"指针回溯"),同时模式串向后移动一个字符的位置.一次次的循环,直到匹配成功或者程

hdu 2594 java实现字符串KMP算法

Problem Description Homer: Marge, I just figured out a way to discover some of the talents we weren't aware we had. Marge: Yeah, what is it? Homer: Take me for example. I want to find out if I have a talent in politics, OK? Marge: OK. Homer: So I tak

流动python - 字符串KMP匹配

首先我们看一下简单的字符串匹配. 你可以把文本字符串s固定,模式字符串p从s对齐的左边缘,作为承担部分完全一致,匹配成功,失败将是模式字符串p整体向右1地点,继续检查对齐部分,重复. #朴素匹配 def naive_match(s, p): m = len(s); n = len(p) for i in range(m-n+1):#起始指针i if s[i:i+n] == p: return True return False 关于kmp算法,讲的最好的当属阮一峰的<字符串匹配的KMP算法>.

【poj 1961】Period(字符串--KMP循环节)

题意:给你一个字符串,求这个字符串到第 i 个字符为止的重复子串的个数. 解法:判断重复子串的语句很重要!!if (p && i%(i-p)==0) printf("%d %d\n",i,i/(i-p)); 我之前一直不是很理解,而实际上多枚举几种情况就好了.若是重复的,那么next[i]肯定是最大值,值余下一个循环节不同:而若不是,next[i]表示的前缀和后缀串的和重叠部分不一样以外的部分就肯定空出来,不能整除的.(P.S.我在说些什么......m(._.)m)

【数据结构】 字符串&amp;KMP子串匹配算法

字符串 作为人机交互的途径,程序或多或少地肯定要需要处理文字信息.如何在计算机中抽象人类语言的信息就成为一个问题.字符串便是这个问题的答案.虽然从形式上来说,字符串可以算是线性表的一种,其数据储存区存储的元素是一个个来自于选定字符集的字符,但是字符串由于其作为一个整体才有表达意义的这个特点,显示出一些特殊性.人们一般关注线性表都会关注其元素和表的关系以及元素之间的关系和操作,而字符串常常需要一些对表整体的关注和操作. 字符串的基本概念如长度,比大小,子字符串等等这些,只要有点编程基础的人都懂就不

字符串 - KMP算法

字符串算法中,字符串匹配是一个非常重要的应用.例如在网页中查找关键词,其实就是在对字符串匹配,也就是看一个主字符串中是否包含了一个子字符串. 而KMP算法在字符串匹配方法中一个很著名并且很聪明的算法,当然也确实比较难理解.甚至于有程序员因为无法理解KMP算法而直接改用暴力匹配.本身自己学算法起步较晚,第一次接触到KMP算法已经是研究生毕业一年了.虽然带着研究生的学历背景,但是刚开始看的时候依然是一脸懵逼.看了很多博主的讲解总算是明白了,所以在这篇博客中记录下来自己的理解,如果能帮助到别人也是万分

字符串-我寄愁心与明月

字符串 KMP Fail 完了. Suffix Automaton Right/Endpos Longest/Mx & Shortest/Mn 两个状态要么是包含关系,要么无交集. 求拓扑序等同于对 Mx 排序. 求 Right 集合大小:新加点时 V[x] = 1 ,拆出来的新点权值为零,拓扑排序之后 V[Par[x]] += V[x] . 求 Right 集合元素:类似求大小,拓扑排序之后更新父亲,用数据结构去合并.新加点时 V[x] = 1, Right[x].insert(Tot) (