\(\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. 【逆向】CVE-2017-8570漏洞分析调试技巧

    前言 CVE-2017-8570是一个逻辑型漏洞,该漏洞利用复合Moniker绕过了CVE-2017-0199的更新补丁,可以在Office文档中执行任意SCT(Windows Script Comp ...

  2. nuxt项目npm install 或安装sass时报错

    初始化nuxt项目时,多人开发,同事提前安装的sass ,拉去代码初始化npm install 时提示gyp版本有问题.找了好多方法,最后还是将node.js版本降低了.原来是16.13.2降低为14 ...

  3. 【Android HttpClient引入】感慨下自己看的Android教程有点老了

    教程看到使用HttpClient,发现没有继承该类. 原因是API23即在Android 6.0(API 23) 后,Google已经移除了Apache HttpClient 相关类,推荐使用Http ...

  4. JAVA 作业

    1.让用户分2次输入2个整数,输出2个数的最大值,最小值 import java.util.Scanner;class Demo01 { public static void main(String[ ...

  5. 30day_网络编程

    由于不同机器上的程序要通信,于是产生通信 C/S架构: Client与Server,客户端(只有用的时候再使用)与服务端(一直运行,等待服务) B/S架构: 浏览器端与服务器端 Browser浏览器, ...

  6. Linux配置

    JDK安装 一.登录虚拟机进入终端切换到root用户,输入:su 接着输入密码 再输入:cd - 回到root用户 二.接着在终端进入对应文件新建一个装jdk包的文件夹,输入:mkdir +目录名称 ...

  7. LeetCode 之 108. 将有序数组转换为二叉搜索树

    原题链接 思路: 二叉搜索树的定义: 它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值均大于它的 ...

  8. C++11的override、default和delete关键字

    最近在参与组里的项目时接触了很多以前自己没太了解的C++语法(尤其是C++11以后出现的),今天给大家讲一下C++11新出的override和default关键字. override关键字主要在声明类 ...

  9. 艾思最新案例分享:塔蓝物流app-物流仓储管理系统app. app开发

    塔蓝物流app是一款物流仓储管理app:主要业务范围空运,海运,进出口货物及过境货物的运输代理,包括揽物订舱,仓储(危险品除外),包装,搬运装卸,中转,流通加工,集装箱拼装拆箱(危险品除外),结算运杂 ...

  10. Docker+jenkins 运行 python 自动化

    一.实现思路 在 Linux 服务器安装 docker 创建 jenkins 容器 根据自动化项目依赖包构建 python 镜像(构建自动化 python 环境) 运行新的 python 容器,执行 ...