- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
KMP是一种用于模式串匹配问题的算法。 给出一个文本串和模式串,查询模式串在文本串中的(出现次数、出现位置等等)的问题称为“模式串匹配问题”。 KMP算法的本质是:针对模式串构建一个特定的数组,用于在匹配失败时减少后续匹配过程中的无用比较,可以将时间复杂度优化到 线性 .
设文本串为 \(s\) ,长度为 \(n\) ;模式串为 \(t\) ,长度为 \(m\) 。 预处理一个 \(next\) 数组,对于 \(next[i]\) ,它表示在 \(t\) 的前 \(i\) 个字母中,最长公共前后缀的长度。 什么意思呢?我们举个栗子: 比如 \(t\) 是 \(ababaca\) ,则对应的 \(next\) 数组如下所示:
\(i\) | \(1\) | \(2\) | \(3\) | \(4\) | \(5\) | \(6\) | \(7\) |
---|---|---|---|---|---|---|---|
\(next[i]\) | \(0\) | \(0\) | \(1\) | \(2\) | \(3\) | \(0\) | \(1\) |
\(next[1]\) :前缀:{空};后缀:{空}。 \(next\) : \(0\) \(next[2]\) :前缀:{ \(a\) };后缀:{ \(b\) }。 \(next\) : \(0\) \(next[3]\) :前缀:{ \(\color{red}a\) , \(ab\) };后缀:{ \(\color{red}a\) , \(ba\) }。 \(next\) : \(1\) \(next[4]\) :前缀:{ \(a\) , \(\color{red}ab\) , \(aba\) };后缀:{ \(b\) , \(\color{red}ab\) , \(bab\) }。 \(next\) : \(2\) \(next[5]\) :前缀:{ \(a\) , \(ab\) , \(\color{red}aba\) , \(abab\) };后缀:{ \(a\) , \(ba\) , \(\color{red}aba\) , \(baba\) }。 \(next\) : \(3\) \(next[6]\) :前缀:{ \(a\) , \(ab\) , \(aba\) , \(abab\) , \(ababa\) };后缀:{ \(c\) , \(ac\) , \(bac\) , \(abac\) , \(babac\) }。 \(next\) : \(0\) \(next[7]\) :前缀:{ \(\color{red}a\) , \(ab\) , \(aba\) , \(abab\) , \(ababac\) };后缀:{ \(\color{red}a\) , \(ca\) , \(aca\) , \(baca\) , \(abaca\) , \(babaca\) }。 \(next\) : \(1\) 。
这里我们先假设我们已经求出了 \(next\) 数组(马上讲怎么求),我们来看看怎么进行匹配。 因为“next”这个词有可能被判断为关键字,所以接下来我们用“ne”来表示。 首先我们建立两个指针,分别为文本串的和模式串的.
int i = 0, j = 0;
然后,当文本串的指针还没有到达终点时,我们就接着匹配.
while (i < s.size()) {
...
}
如果当前的字母匹配,那就把两个指针都往后移一个字符.
if (s[i] == t[j]) i++, j++;
如果不匹配,那就把 \(j\) 挪动 \(next[j - 1]\) 格.
else if (j > 0) j = ne[j - 1];
如果还是不行,说明这是第一格就不匹配,把 \(i\) 往后挪.
else i++;
如果 \(j\) 到了终点,说明匹配成功。返回模式串匹配的起始坐标即可.
if (j == t.size()) return i - j;
刚才我们说了:如果不匹配,那就把 \(j\) 挪动 \(next[j - 1]\) 格。为什么可以这么做呢?
(这里用了一个b站大佬的图) 如上图,可以发现,我们成功匹配的画黄线的两个 \(AB\) ,和前面跳过的画蓝线的两个 \(AB\) 是完全一样的。所以我们可以直接跳过后两个 \(AB\) 接着匹配;这也就证实了 \(next\) 数组的本质:最长公共前后缀的长度(注意:最长公共前后缀不可以是子串本身,否则我们的移动就没有意义了).
我们可以用递推的方式来求解 \(next\) 数组,比如我们现在已经知道当前的公共前后缀了,接下来分两种情况讨论:
那么这样我们的代码实现就很简单了:
void buildnext(string t) {
memset(ne, 0, sizeof ne);
int len = 0; // 当前公共前后缀的长度
int i = 1;
while (i < t.size()) { // 依次生成每个next数值
if (t[len] == t[i]) { // 下一个字符依然相同
len++; // 长度+1
ne[i] = len;
i++;
} else {
if (len == 0) {
ne[i] = 0; // 不存在直接为0
i++;
} else len = ne[len - 1]; // 是否存在更短的前后缀
}
}
}
最后放一下题和代码
#include <bits/stdc++.h>
using namespace std;
int ne[100010];
void buildnext(string t) {
memset(ne, 0, sizeof ne);
int len = 0;
int i = 1;
while (i < t.size()) {
if (t[len] == t[i]) {
len++;
ne[i] = len;
i++;
} else {
if (len == 0) {
ne[i] = 0;
i++;
} else len = ne[len - 1];
}
}
}
void kmp(string s, string t) {
buildnext(t);
int i = 0, j = 0;
while (i < s.size()) {
if (s[i] == t[j]) i++, j++;
else if (j > 0) j = ne[j - 1];
else i++;
if (j == t.size()) cout << i - j << ' ';
}
}
int main() {
int n, m;
string s, t;
cin >> n >> t >> m >> s;
kmp(s, t);
return 0;
}
码字不易qwq 如果觉得这篇文章还不错的话,请点个赞吧! 如果有任何问题,欢迎在评论区提问,我会尽可能的第一时间回复! 。
最后此篇关于KMP字符串匹配算法的文章就讲到这里了,如果你想了解更多关于KMP字符串匹配算法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
如何使用 SPListCollection.Add(String, String, String, String, Int32, String, SPListTemplate.QuickLaunchO
我刚刚开始使用 C++ 并且对 C# 有一些经验,所以我有一些一般的编程经验。然而,似乎我马上就被击落了。我试过在谷歌上寻找,以免浪费任何人的时间,但没有结果。 int main(int argc,
这个问题已经有答案了: In Java 8 how do I transform a Map to another Map using a lambda? (8 个回答) Convert a Map>
我正在使用 node + typescript 和集成的 swagger 进行 API 调用。我 Swagger 提出以下要求 http://localhost:3033/employees/sear
我是 C++ 容器模板的新手。我收集了一些记录。每条记录都有一个唯一的名称,以及一个字段/值对列表。将按名称访问记录。字段/值对的顺序很重要。因此我设计如下: typedef string
我需要这两种方法,但j2me没有,我找到了一个replaceall();但这是 replaceall(string,string,string); 第二个方法是SringBuffer但在j2me中它没
If string is an alias of String in the .net framework为什么会发生这种情况,我应该如何解释它: type JustAString = string
我有两个列表(或字符串):一个大,另一个小。 我想检查较大的(A)是否包含小的(B)。 我的期望如下: 案例 1. B 是 A 的子集 A = [1,2,3] B = [1,2] contains(A
我有一个似乎无法解决的小问题。 这里...我有一个像这样创建的输入... var input = $(''); 如果我这样做......一切都很好 $(this).append(input); 如果我
我有以下代码片段 string[] lines = objects.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.No
这可能真的很简单,但我已经坚持了一段时间了。 我正在尝试输出一个字符串,然后输出一个带有两位小数的 double ,后跟另一个字符串,这是我的代码。 System.out.printf("成本:%.2
以下是 Cloud Firestore 列表查询中的示例之一 citiesRef.where("state", ">=", "CA").where("state", "= 字符串,我们在Stack O
我正在尝试检查一个字符串是否包含在另一个字符串中。后面的代码非常简单。我怎样才能在 jquery 中做到这一点? function deleteRow(locName, locID) { if
这个问题在这里已经有了答案: How to implement big int in C++ (14 个答案) 关闭 9 年前。 我有 2 个字符串,都只包含数字。这些数字大于 uint64_t 的
我有一个带有自定义转换器的 Dozer 映射: com.xyz.Customer com.xyz.CustomerDAO customerName
这个问题在这里已经有了答案: How do I compare strings in Java? (23 个回答) 关闭 6 年前。 我想了解字符串池的工作原理以及一个字符串等于另一个字符串的规则是
我已阅读 this问题和其他一些问题。但它们与我的问题有些无关 对于 UILabel 如果你不指定 ? 或 ! 你会得到这样的错误: @IBOutlet property has non-option
这两种方法中哪一种在理论上更快,为什么? (指向字符串的指针必须是常量。) destination[count] 和 *destination++ 之间的确切区别是什么? destination[co
This question already has answers here: Closed 11 years ago. Possible Duplicates: Is String.Format a
我有一个Stream一个文件的,现在我想将相同的单词组合成 Map这很重要,这个词在 Stream 中出现的频率. 我知道我必须使用 collect(Collectors.groupingBy(..)
我是一名优秀的程序员,十分优秀!