Day7: KMP与Trie树

\[Designed\ By\ FrankWkd\ -\ Luogu@Lwj54joy,uid=845400
\]

特别感谢 此次课的主讲 - Kwling

KMP算法

  • \(KMP (Knuth–Morris–Pratt)\) 是一个字符串匹配算法,于1977年由上述三人共同发表。

    在线性的时空复杂度内解决字符串匹配。

字符串匹配

  • 给定两个字符串 \(s,t\)(通常来讲我们管较短的串叫做“模式串”,长的叫“匹配串”。我们的任务是在长串内找到短串)。

  • 我们定义记号 \(s[l,r]\) 表示的是 \(s\) 串,从下标 \(l\) 到下标 \(r\) 的子串。(比如说 \(s=“abcabca”\),那么 \(s[2,4]=“bca”,1-index\))

  • 字符串匹配:我们希望对于 s(长串)的每一个前缀 s[1,i],找到最大的 j 使得 s[i-j+1,i]=t[1,j]

  • 例:\(s=“ababc”,t=“aba”:\)

    \(i=1: s[1,1]=t[1,1]=“a” ; i=2:\) \(s[1,2]=t[1,2]=“ab”\)

    \(i=3: s[1,3]=t[1,3]=“abc”\)(成功匹配); \(i=4: s[3,4]=t[1,2]=“ab”\)

    \(i=5: s[6,5]=t[1,0]=\phi\)  (未成功匹配,空串)

解法

  • 我们希望能够复用尽量多的信息。
  • 假设我们此时知道 \(s[1,i]\) 对应的匹配长度 \(k_i\),怎么求出 \(s[1,i+1]\) 的匹配长度?
  • 大致思路:先看看 \(s[i+1]\) 和 \(t[k_i+1]\) 一不一样。
    • 一样:长度++
    • 不一样:模式串前移 \(1\) 位

Border串

  • 我们注意到,实际上我们是在找 \(t[1,j]\) 的一个既是其前缀又是其后缀的最长的子串。(当然需要不是 \(t[1,j]\) 本身)

  • \(s=“abab”\) 那么 \(s[1,2]=“ab”\) 就是这样的串。

    即是前缀又是后缀串的名字是:\(border\) 串。

  • 我们可以发现 \(border\) 集合:\(S_i=S_k+{k}\) ( \(k\) 是 \(t[1,i]\) 的最长border)。

    • 例:\(BS(“ababa”)={“a”,“aba”}=BS(“aba”)+{“aba”}\)

      其中 \(BS\) 是 \(border\ set\) 的意思(即所有的 \(border\) 串构成的集合)
  • 这实际上也就是说:\(border\) 集合实际上是一条链

  • 我们不断地去找串 \(S\) 的最长 \(border T\),然后把他加入集合,把 \(S\) 变成 \(T\) 直到 \(S\) 为空。

  • 现在问题变为:如何求出最长 \(border\)?

  • 假设我们知道了 \(s[1,i]\) 的 \(border\),怎么求出 \(s[1,i+1]\) 的 \(border\) 长度?

    我们不断地“跳” \(t[1,i]\) 的 \(border\) 来找到 \(t[1,i+1]\) 的 最长 \(border\)。

复杂度

  • 看起来是 \(O(n^2)\) 的,但实则不然。

  • 考虑每次最多让 \(border\) 链 变长 \(1\) ,以 \(border\) 链 长度作为“势能”,便可以分析出 \(O(n)\) 的摊还复杂度。

  • 另一边的字符串匹配亦是同理。(算法,复杂度分析)

  • 假设 \(s[i-j+1,i]\) 匹配到了 \(t[1,j]\)。

    • 我们不断地去 “跳” \(t[1,j]\) 的 \(border\),找到一个 \(t[1,k]\) 使得 \(t[k+1]=s[i+1]\)。
    • 这样就完成了一次字符串匹配。
  • 我们不难分析出,字符串匹配的复杂度为:\(O(n+m)=O(|S|+|T|)\)

  • 不难发现,实际上 border 集合的结构,可以用一颗树来描述。

  • 以 S=“abababa” 举例。

    • \(S[1,1]\) 对应最长 \(border=\phi;\)
    • \(S[1,2]\) 对应最长 \(border=\phi 。\)
    • \(S[1,3]\) 对应最长 \(border=“a”;\)
    • \(S[1,4]\) 对应最长 \(border=“ab”。\)
    • \(S[1,5]\) 对应最长 $border=“aba”; $
    • \(S[1,6]\) 对应最长 \(border=“abab”。\)
    • \(S[1,7]\) 对应最长 \(border=“ababa”。\)
  • 如图,这棵树具有很优良的性质。

  • 可以发现,我们求解border的算法实际上不过是在 这棵树上进行 dfs。

Trie树

  • 我们有这样一个“数据结构”能够去“维护”给定的“串集合” \(S\)。并且能支持“查询”一个串在不在这个“串集合” \(S\) 中。(¥S$ 即为字典)
  • 很好理解,如图所示:假设字典 \(S={“she”,“he”,“shy”,“sheep”,“sheet”}\)
    • 那么字典树将如图所示:

    • 每一条边对应一个字母,每一个结点对应一个单词的前缀。
    • \(0\) 结点的含义是空串。
  • 查询就是从 \(0\) 开始查找有没有对应字母的出边。

01Trie树

  • \(0-1\)字典树可以很方便的维护形如“最大异或值”这类位运算查询。
  • 我们将一个 \(32/64\) 位整数看成一个长为 \(32/64\) 的字符串。
  • 例: \(4=(0…0100), 6=(0…0110), 11=(0…01011)\),将他们插入 \(trie\) 中:
    • 特点:所有叶子等深。
  • 假如我们想查询 \(x\) 与集合里的数最大异或值。
  • 比如说 $x=8=(1000) $,我们从高往低按位考虑。
    • 在 \(root\) 处的出边是 \(1\),我们看 \(root\) 有无 \(0\) 的出边,
    • 然后走向“结点 \(0\)”。(这样才能让异或值最大)
    • 接下来同理,我们在“结点 \(0\)” 看有没有 \(1\) 的出边……

题目&参考解法

#include<bits/stdc++.h>
using namespace std;
int nextl[1001]={0};//这个数组名不能直接用next,容易报错
string a,b;
int KMP(){
int i=0,j=0,we=0;
int as=a.size(),bs=b.size();
while(i<as){
if(a[i]==b[j]||j==-1){//当单个字符匹配成功或前面没东西
i++;
j++;//i,j都往前挪
if(j==bs){//字符串匹配成功
we++;
j=0;//子串归0
}
}else{
j=nextl[j];//上next数组,移动到下一个位置
}
}
return we;
}
void Next(){//计算next数组值
int bs=b.size();
nextl[0]=-1;//代表前面没东西
int j=0,k=-1;
while(j<bs){
if(k==-1||b[j]==b[k]){//当前面没有东西或j、k对应的字符一样
nextl[j++]=k++;//直接往后挪一位,因为前几位字符=后几位字符或前面啥也没有
}else{
k=nextl[k];
}
}
}
int main(){
while(cin>>a){
if(a=="#"){
break;
}else{
cin>>b;
Next();
cout<<KMP()<<endl;
}
}
}

2025dsfz集训Day7: KMP与Trie树的更多相关文章

  1. 字符串算法--$\mathcal{KMP,Trie}$树

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

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

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

  3. POJ3376 Finding Palindromes —— 扩展KMP + Trie树

    题目链接:https://vjudge.net/problem/POJ-3376 Finding Palindromes Time Limit: 10000MS   Memory Limit: 262 ...

  4. [知识点]Trie树和AC自动机

    // 此博文为迁移而来,写于2015年5月27日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w1s8.html 1.前 ...

  5. 「2017 山东三轮集训 Day7 解题报告

    「2017 山东三轮集训 Day7」Easy 练习一下动态点分 每个点开一个线段树维护子树到它的距离 然后随便查询一下就可以了 注意线段树开大点... Code: #include <cstdi ...

  6. AC自动机——1 Trie树(字典树)介绍

    AC自动机——1 Trie树(字典树)介绍 2013年10月15日 23:56:45 阅读数:2375 之前,我们介绍了Kmp算法,其实,他就是一种单模式匹配.当要检查一篇文章中是否有某些敏感词,这其 ...

  7. 从Trie树(字典树)谈到后缀树

    转:http://blog.csdn.net/v_july_v/article/details/6897097 引言 常关注本blog的读者朋友想必看过此篇文章:从B树.B+树.B*树谈到R 树,这次 ...

  8. 算法笔记--字典树(trie 树)&& ac自动机 && 可持久化trie

    字典树 简介:字典树,又称单词查找树,Trie树,是一种树形结构,是哈希树的变种. 优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较. 性质:根节点不包含字符,除根节点外每一个 ...

  9. 2017 山东二轮集训 Day7 国王

    2017 山东二轮集训 Day7 国王 题目大意 给定一棵树,每个点有黑白两种颜色,定义一条简单路径合法当且仅当路径上所有点黑色与白色数量相等,求有多少非空区间 \([L,R]\) ,使得所有编号 \ ...

  10. hiho一下 第二周&第四周:从Trie树到Trie图

    hihocoder #1014 题目地址:http://hihocoder.com/problemset/problem/1014 hihocoder #1036 题目地址: http://hihoc ...

随机推荐

  1. 从装水瓶到接雨水:一道经典动态规划问题的深度解析|LeetCode 42 接雨水

    LeetCode 42 接雨水 点此看全部题解 LeetCode必刷100题:一份来自面试官的算法地图(题解持续更新中) 生活中的算法 你有没有注意过,很多户外运动的水壶都有不规则的凹凸形状?这些凹凸 ...

  2. EIP、VIP、RIP的区别

    本文分享自天翼云开发者社区<EIP.VIP.RIP的区别>,作者:魏****猛 EIP.VIP.RIP都是网络中经常使用的术语,但是它们的意义和使用场景是不同的. 首先,EIP(Elast ...

  3. SQL注入之联合查询注入

    SQL注入之联合查询注入 一.联合查询注入原理 联合查询注入是一种常见的SQL注入攻击手法,其核心原理是利用SQL中的UNION操作符将多个SELECT语句的结果集合并,从而返回一个统一的结果集.在使 ...

  4. linux更新软件源 安装docker

    vim /etc/aptsources.list 学习参考 https://blog.csdn.net/bskfnvjtlyzmv867/article/details/81044217   # 默认 ...

  5. 创建Linux iscsi target存储

    配置targetcli 创建存储对象 创建块存储对象            cd /backstores/block        \          create block1 dev=/dev/ ...

  6. DeepSeek提示词工程完全指南:如何用「思维翻译器」激发大模型潜能——附官方提示词和优化案例

    DeepSeek提示词工程完全指南:如何用「思维翻译器」激发大模型潜能--附官方提示词和优化案例 字数:约3000字|预计阅读时间:8分钟 之前写了一篇DeepSeek-R1 技术全景解析:从原理到实 ...

  7. C#下.NET配置文件的使用(1)

    原文链接:https://blog.csdn.net/dbzhang800/article/details/7212420 System.Configuration 命名空间中的东西是为读写应用程序的 ...

  8. JUC并发—8.并发安全集合一

    大纲 1.JDK 1.7的HashMap的死循环与数据丢失 2.ConcurrentHashMap的并发安全 3.ConcurrentHashMap的设计介绍 4.ConcurrentHashMap的 ...

  9. 动态能力理论&知识管理理论--商业之所见

    动态能力理论:企业整合,建立和再配置内外部资源以适应快速变化环境的能力. (1)"动态"指的是适应不断变化的环境,企业必须具有不断更新自身能力的能力: (2)"能力&qu ...

  10. MySQL - [04] 分布式部署&主从复制&读写分离

    一.前言 Q1:为什么需要主从复制? 1.在业务复杂的系统中,有一条SQL语句需要锁表,导致暂时不能使用读的服务,那么就很影响运行中的业务.使用主从复制,让主库负责写,从库负责读,这样即使主库出现了锁 ...