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

【字符串】KMP的更多相关文章

  1. hdu 5510 Bazinga(字符串kmp)

    Bazinga Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Sub ...

  2. hdu1686字符串kmp

    The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter 'e ...

  3. 模板—字符串—KMP(单模式串,单文本串)

    模板—字符串—KMP(单模式串,单文本串) Code: #include <cstdio> #include <cstring> #include <algorithm& ...

  4. 字符串 --- KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组

    涉及到字符串的问题,无外乎这样一些算法和数据结构:自动机 KMP算法 Extend-KMP 后缀树 后缀数组 trie树 trie图及其应用.当然这些都是比较高级的数据结构和算法,而这里面最常用和最熟 ...

  5. 【poj 3080】Blue Jeans(字符串--KMP+暴力枚举+剪枝)

    题意:求n个串的字典序最小的最长公共子串. 解法:枚举第一个串的子串,与剩下的n-1个串KMP匹配,判断是否有这样的公共子串.从大长度开始枚举,找到了就break挺快的.而且KMP的作用就是匹配子串, ...

  6. 数据结构(复习)---------字符串-----KMP算法(转载)

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

  7. 字符串(KMP):BZOJ 3670 [Noi2014]动物园

    3670: [Noi2014]动物园 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1521  Solved: 813[Submit][Status] ...

  8. HDU 4668 Finding string (解析字符串 + KMP)

    转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove 题意:给出一个压缩后的串,以及一个模式串,问模式串 ...

  9. 流动python - 字符串KMP匹配

    首先我们看一下简单的字符串匹配. 你可以把文本字符串s固定,模式字符串p从s对齐的左边缘,作为承担部分完全一致,匹配成功,失败将是模式字符串p整体向右1地点,继续检查对齐部分,重复. #朴素匹配 de ...

  10. 查找子字符串----KMP算法深入剖析

    假设主串:a b a b c a b c a c b a b      子串:a b c a c 1.一般匹配算法 逐个字符的比较,匹配过程如下: 第一趟匹配 a b a b c a b c a c ...

随机推荐

  1. HyperLogLog算法分析及其应用

    HyperLogLog 算法的原理讲解以及 Redis 是如何应用它的 探索HyperLogLog算法(含Java实现) 神奇的HyperLogLog算法 Sketch of the Day: Hyp ...

  2. 奥展项目笔记02--一个bat文件运行多个java jar包

    奥展项目中后端微服务有很多jar包,一个一个启动又费时间效率又低,怎么才能一下让所有的jar包一块运行呢?我们可以编写.bat文件来一键启动. 1.我们将.bat文件放到jar包的同一级目录文件夹中: ...

  3. 【学习笔记】字符串—马拉车(Manacher)

    [学习笔记]字符串-马拉车(Manacher) 一:[前言] 马拉车用于求解连续回文子串问题,效率极高. 其核心思想与 \(kmp\) 类似:继承. --引自 \(yyx\) 学姐 二:[算法原理] ...

  4. python 练习题:计算的BMI指数,并根据BMI指数条件选择

    小明身高1.75,体重80.5kg.请根据BMI公式(体重除以身高的平方)帮小明计算他的BMI指数,并根据BMI指数:低于18.5:过轻18.5-25:正常25-28:过重28-32:肥胖高于32:严 ...

  5. 性能监控工具的配置及使用 - Spotlight On Oracle(oracle)

    一.    Spotlight On Oracle(oracle)1.1.   工具简介Spotlight是一个强有力的Oracle数据库实时性能诊断工具,提供了一个直观的.可视化的数据库活动展现.S ...

  6. vue中的---MVVM(面试必问)

    M---Model  (数据) V---View (视图) VM---VIewModel  (转换器) VIewModel主要做两件事: 1.把 Model 中的数据绑定到View(视图层). 2.监 ...

  7. ARM开发板上查看动态库或者可执行程序的依赖关系

    以ARM32开发板为例,在/lib下有一个名为ld-linux-armhf.so.3的可执行程序(在ARM64开发板上是/lib/ld-linux-aarch64.so.1),这个程序负责加载可执行程 ...

  8. Java使用枚举来消除if else(转载)

    Java代码里经常出现多个if else会大大降低效率,我们可以使用枚举的方式来代替if else,示例代码如下: public enum Grade { A_10_90(90, 100, " ...

  9. 良心送分题(牛客挑战赛35E+虚树+最短路)

    目录 题目链接 题意 思路 代码 题目链接 传送门 题意 给你一棵树,然后把这棵树复制\(k\)次,然后再添加\(m\)条边,然后给你起点和终点,问你起点到终点的最短路. 思路 由于将树复制\(k\) ...

  10. msyql常用命令

    1.创建.删除数据库 create database dbname; drop database dbname; 2.选择某一个数据库 use dbname; 3.显示所有表 show tables; ...