\(\mathcal{KMP算法}\)

实际上,完全没必要从\(S\)的每一个字符开始,暴力穷举每一种情况,\(Knuth、Morris\)和\(Pratt\)对该算法进行了改进,称为KMP算法。

而\(KMP\)的精髓在于,对于每次失配之后,我都不会从头重新开始枚举,而是根据我已经得知的数据,从某个特定的位置开始匹配;而对于模式串的每一位,都有唯一的“特定变化位置”,这个在失配之后的特定变化位置可以帮助我们利用已有的数据不用从头匹配,从而节约时间。

特点:1. \(i\) 不回退 2. \(j\) 回退的位置有讲究 3.构建一个辅助数组( \(nxt\) 数组)来跳过不必要的字符比较,从而提高搜索速度。

\(\mathcal{实现流程}\)

为了清楚地表述目的, \(T\) 与 \(S\) 失配前的部分作为 \(T'\) 来表述,此时寻找下一个开始匹配的标志头。而找到下一个标志头的方式为:

找到 \(T'\) 的最长相同前缀与后缀

\(\color{red}{这样找所有的前缀和后缀比较,是不是也是暴力穷举??那该怎么办呢??}\)

\(\color{red}{ans:当然是要用到动态规划递推啦。}\)

\(\mathcal{构建 Nxt 数组}\)

\(nxt\) 数组用于表示当前字符匹配失败时,模式串应该回退到哪个位置。对于模式串 \(p\) ,我们遍历其每个字符,并用一个指针 \(j\) 表示已匹配的字符数。当模式串中的两个字符匹配时,我们更新指针 \(j\) 的值,否则,我们回退 \(j\) 到 \(nxt[j]\) 的位置。通过这种方式,我们可以为模式串构建一个 \(nxt\) 数组,其中 \(nxt[i]\) 表示当模式串中第 \(i\) 个字符匹配失败时,应该回退到的位置。

\(\mathcal{实际字符匹配过程}\)

我们使用两个指针 \(i\) 和 \(j\) 分别遍历原字符串 \(s\) 和模式串 \(p\) 。如果当前字符匹配,则同时移动 \(i\) 和 \(j\) 。如果字符不匹配,我们根据 \(nxt\) 数组回退 \(j\) 的位置,直到找到匹配的字符或回退到模式串的开头。当 \(j\) 等于模式串长度 \(m\) 时,表示找到了一个匹配,输出匹配位置,并将 \(j\) 重置为 \(0\) 。

\(\mathcal{模板代码实现}\)

#include <iostream>
using namespace std;
const int N = 1e+6 + 10;
int nxt[N], n, m;
char p[N], s[N];
int main()
{
cin >> n >> s + 1 >> m >> p + 1;
// build next arraylist
for (int i = 2, j = 0; i <= m; i++)
{
while (j && p[i] != p[j + 1]) j = nxt[j];
if (p[i] == p[j + 1]) j++;
nxt[i] = j;
}
// marry the str
for (int i = 1, j = 0; i <= n; i++)
{
while (j && s[i] != p[j + 1]) j = nxt[j];
if (s[i] == p[j + 1]) j++;
if (j == m) {
cout << i - m << " ";
j = 0;
}
}
cout << endl;
return 0;
}

\(\mathcal{Trie\,树}\)

字典树是一种高效的字符串数据结构,尤其适用于处理大量字符串的时候,它通过将字符串的公共前缀合并在一起,节省空间并提高查询速度。

\(\mathcal{实现流程}\)

\(\mathcal{初始化变量和数据结构}\)

定义一个字典树结构( \(tree\) 数组)和一个记录字符串出现次数的数组( \(vis\) 数组)。同时定义一个计数器 \(flag\) 用于记录字典树中节点的数量。二维数组 \(tree\) 表示字典树的结构,其中 \(tree[i][j]\) 表示第 \(i\) 个节点的第 \(j\) 个子节点。

\(\mathcal{子功能实现}\)

\(\mathcal{insert}\)

实现一个 \(insert\) 函数,用于向字典树中插入一个字符串。它遍历字符串中的每个字符,将字符转换为数组下标(通过减去' \(a\) '并加上 \(1\) )。如果当前字符对应的子节点不存在,则创建一个新的节点并更新节点计数器。最后,在字符串末尾的节点中,更新字符串出现的次数。

\(\mathcal{query}\)

实现一个 \(query\) 函数,用于查询字典树中字符串的出现次数。它遍历字符串中的每个字符,将字符转换为数组下标。如果当前字符对应的子节点不存在,说明字符串不存在,查询结束。否则,将指针移动到子节点。最后,返回字符串末尾节点对应的出现次数。

\(\mathcal{主程序逻辑}\)

读取操作数量 \(n\) ,然后循环处理每个操作。对于每个操作,读取操作类型( \(ope\) )和操作字符串( \(str\) )。如果操作类型为 "\(i\)" ,调用 \(insert\) 函数插入字符串;如果操作类型为其他(例如查询操作),调用 \(query\) 函数查询字符串,并输出查询结果。

\(\mathcal{模板代码实现}\)

#include <iostream>
using namespace std;
const int N = 1e+6 + 10;
int n, flag = 1;
string ope, str;
int tree[N][27], vis[N][27];
void insert(string str)
{
int pos = 0;
int tmp = 0;
for (int i = 0; i < str.size(); i++)
{
tmp = str[i] - 'a' + 1;
if (tree[pos][tmp] == 0) tree[pos][tmp] = flag ++;
pos = tree[pos][tmp];
}
vis[pos][tmp] ++;
}
int query(string str)
{
int pos = 0;
int tmp = 0;
for (int i = 0; i < str.size(); i++)
{
tmp = str[i] - 'a' + 1;
if (tree[pos][tmp] == 0) break;
pos = tree[pos][tmp];
}
return vis[pos][tmp];
}
int main()
{
cin >> n;
while (n--)
{
cin >> ope >> str;
if (ope == "i") insert(str);
else cout << query(str) << endl;
}
return 0;
}

字符串算法--$\mathcal{KMP,Trie}$树的更多相关文章

  1. 数据结构学习之字符串匹配算法(BF||KMP)

    数据结构学习之字符串匹配算法(BF||KMP) 0x1 实验目的 ​ 通过实验深入了解字符串常用的匹配算法(BF暴力匹配.KMP.优化KMP算法)思想. 0x2 实验要求 ​ 编写出BF暴力匹配.KM ...

  2. 字符串匹配算法之 kmp算法 (python版)

    字符串匹配算法之 kmp算法 (python版) 1.什么是KMP算法 KMP是三位大牛:D.E.Knuth.J.H.MorriT和V.R.Pratt同时发现的.其中第一位就是<计算机程序设计艺 ...

  3. 【LOJ#2507】[CEOI2011]Matching(KMP,树状数组)

    [LOJ#2507][CEOI2011]Matching(KMP,树状数组) 题面 LOJ 题解 发现要做的是排名串的匹配. 然后我们考虑把它转成这个位置之前有多少个数小于当前这个数,这样子只要每个位 ...

  4. [BZOJ 1535] [Luogu 3426]SZA-Template (KMP+fail树+双向链表)

    [BZOJ 1535] [Luogu 3426]SZA-Template (KMP+fail树+双向链表) 题面 Byteasar 想在墙上涂一段很长的字符,他为了做这件事从字符的前面一段中截取了一段 ...

  5. bzoj5130 字符串的周期(kmp,最小表示法)

    bzoj5130 字符串的周期(kmp,最小表示法) bzoj 题解时间 m很大,n很小. 周期很容易求,就是kmp之后n-fail[n]. 之后对于枚举所有的字符串用最小表示法,暴力搜索. 能过就完 ...

  6. Java数据结构之字符串模式匹配算法---KMP算法2

    直接接上篇上代码: //KMP算法 public class KMP { // 获取next数组的方法,根据给定的字符串求 public static int[] getNext(String sub ...

  7. Java数据结构之字符串模式匹配算法---KMP算法

    本文主要的思路都是参考http://kb.cnblogs.com/page/176818/ 如有冒犯请告知,多谢. 一.KMP算法 KMP算法可以在O(n+m)的时间数量级上完成串的模式匹配操作,其基 ...

  8. 字符串处理:kmp算法

    刷vj的时候遇到一个kmp算法,就学习了一下 看了某位大神的清楚解释略有领会 看了一遍之后,可以清楚的知道 void kmp 的模拟过程,就是j指针的运动情况 但是j指针的运动是如何具体的实现,这其实 ...

  9. 字符串匹配算法之————KMP算法

    上一篇中讲到暴力法字符串匹配算法,但是暴力法明显存在这样一个问题:一次只移动一个字符.但实际上,针对不同的匹配情况,每次移动的间隔可以更大,没有必要每次只是移动一位: 关于KMP算法的描述,推荐一篇博 ...

  10. 字符串匹配算法之kmp算法

    kmp算法是一种效率非常高的字符串匹配算法,是由Knuth,Morris,Pratt共同提出的模式匹配算法,所以简称KMP算法 算法思想 在一个字符串中查找另一个字符串时,会遇到如下图的情况 我们通常 ...

随机推荐

  1. RStudio中有常用的快捷键

    1.常用快捷键 转自:https://blog.csdn.net/swuteresa/article/details/8649067 2.RStudio中如何撤销上一步操作: 一般运行过的程序都会在H ...

  2. Debug --> 箱线图

    箱线图主要用于反映原始数据分布的特征,还可以进行多组数据分布特征的比较. 箱形图最大的优点就是不受异常值的影响,能够准确稳定地描绘出数据的离散分布情况,同时也利于数据的清洗. 在箱图中,最上方和最下方 ...

  3. Elasticsearch 查询小笔记

    2.x 版本,组合多查询https://www.elastic.co/guide/cn/elasticsearch/guide/current/combining-queries-together.h ...

  4. 2月21日python程序设计

    Python变量 1.不需要事先声明变量名及其类型,直接赋值即可. 2.强类型编程语言,根据赋值或运算来推断变量类型. 3.动态类型语言,变量的类型也是可以随时变化的. >>>  x ...

  5. CSS 常用样式-字体属性

    字体类样式我们已经学习过字号font-size.字体font-family两个属性,接下来还有几个常用的字体属性. 粗细 font-weight: 作用:设置文字是否加粗显示. 属性名:font-we ...

  6. gitbash 本地文件提交为一个新的项目 到 gitlab

    此篇操作的环境: 已经配置好一个本地仓库,且可成功的将本地项目提交到gitlab上的对应的远程仓库. 这意味着此时你的电脑已经安装好git,有一个本地仓库存放你的项目,成功配置好一个对应的远程仓库,且 ...

  7. react 微信h5跳转小程序

    componentDidMount() { this.getWxConfig() } getWxConfig () { // 请求后台接口拿到 data信息 wx.config({ debug: fa ...

  8. url not set

    UrI not set 原因与处理方法 今天下午跑代码时发现,上午能跑的代码下午跑不了了.一直报 Url not set错误. 出现这个问题的主要原因,是因为代码中的@ConfigurationPro ...

  9. Window10设置技巧

    1.关闭应用程序上的最近打开文件,效果图 2.固定到"开始"屏幕,效果图 3.任务栏图标太大了

  10. 无显示器无键盘的树莓派搭建NAS(samba)

    使用软件Rufus烧录系统2020-02-13-raspbian-buster.img到TF卡后,在TF卡的文件夹内创建空文件ssh,再创建一个名为wpa_supplicant.conf的文件,内容为 ...