KMP算法

说明

KMP算法是一种比较高效的字符串匹配算法,可以在线性时间内求出一个串在另一个串的所有匹配位置。

解析

详解KMP

设模板串是 \(pattern\) 令 \(next[i] = max\{k|pattern[0...k-1]=pattern[i-k+1...i]\}\), \(next[]\) 可以通过动态规划求解。

板子

\(next\)数组存在的意义:

当 \(A\) 串匹配到 \(i\), \(B\) 串匹配到 \(j\)时, 如果发现失配,可以直接令 \(j = next[i]\) 然后继续匹配, ( \(next[i]\) 将记录如果 \(B\) 串匹配到 \(A\) 的 \(i\) 位置,前面有多少是已经匹配了的。)

\(next\) 数组的求法

算法流程

  1. 初始化 \(next[1] = j = 0\) ,假设 \(next[1...i-1]\) 已经求出,下面求解 \(next[i]\)
  2. 不断尝试扩展长度 \(j\), 如果扩展失败(下一个字符不相等),令 \(j\) 变为 \(next[j]\), 直至 \(j = 0\)(应该从头开始匹配)
  3. 如果扩展成功,匹配长度 \(j\) 就增加 \(1\), \(next[i]\) 的值就是 \(j\)。
  1. inline void calc_next() {
  2. next[1] = 0;
  3. for (int i = 2, j = 0; i <= n; ++ i) {
  4. while (j > 0 && a[i] != a[j + 1]) j = next[j];
  5. if (a[i] == a[j + 1]) j ++;
  6. next[i] = j;
  7. }
  8. }

我们用\(f\)数组记录每个位置能匹配的个数,基于 \(next\) 数组,我们能在 \(O(n + m)\) 的时间处理结果

  1. inline void calc_f() {
  2. for (int i = 1, j = 0; i <= m; ++ i) {
  3. while (j > 0 && (j == n || b[i] != a[j + 1])) j = next[j];
  4. if (b[i] == a[j + 1]) j ++;
  5. f[i] = j;
  6. if (f[i] == n) {/* the first time */}
  7. }
  8. }

拓展KMP算法

说明

在线性复杂度内求出一个串对于另一个串的每个后缀的最长公共前缀

解析

假设两个串是 \(s\) 和 \(p\), 要求 \(p\) 的每个 \(s\) 的后缀的最长公共前缀.

我们可以先求出 \(p\) 与它自己的每个后缀的最长公共前缀(假设为 \(A\))。类似KMP的思想,假设我们现在要计算 \(p\) 的第 \(i\) 个字符开头的后缀,而我们已经得到了 \(A[1...i-1]\), 我们可以找到以前的一个 \(k\) 使得 \(k + A[k] - 1\) 最大(就是匹配到的范围最大),我们可以得知 \(p[1...A[k]] = p[k...k+A[k]-1]\), 于是可以得到 \(p[i...k+A[k]-1] = p[i-k+1...A[k]]\),即我们可以利用 \(A[i - j + 1]\) 的信息。

分两种情况讨论,如果 \(i+A[i-k+1]-1\)比\(k+A[k]-1\)小,则 \(A[k] = A[i - k + 1]\) ,否则暴力扫一次。计算 \(p\) 与 \(s\) 的后缀的最长公共前缀也是类似的方法,可以证明复杂度是线性的。

板子

(未精简版本)

输入:求 \(a\) 关于 \(b\) 的后缀的最长公共前缀, \(Next\) 记录 \(a\)关于自己每个后缀的最长公共前缀, \(ret\) 记录 \(a\) 关于 \(b\) 的后缀的最长公共前缀

  1. void ExtendedKMP(char *a, char *b, int M, int N, int *Next, int *ret) {
  2. int i, j, k;
  3. for (j = 0; 1 + j < M && a[j] == a[1 + j]; ++ j);
  4. Next[1] = j;
  5. k = 1;
  6. for (i = 2; i < M; ++ i) {
  7. int Len = k + Next[k], L = Next[i - k];
  8. if (L < Len - i) {
  9. Next[i] = L;
  10. } else {
  11. for (j = max(0, Len - i); i + j < M && a[j] == a[i + j]; ++ j);
  12. Next[i] = j;
  13. k = i;
  14. }
  15. }
  16. for (j = 0; j < N && j < M && a[j] == b[j]; ++ j);
  17. ret[0] = j;
  18. k = 0;
  19. for (i = 1; i < N; ++ i) {
  20. int Len = k + ret[k], L = Next[i - k];
  21. if (L < Len - i) {
  22. ret[i] = L;
  23. } else {
  24. for (j = max(0, Len - i); j < M && i + j < N && a[j] == b[i + j]; ++ j);
  25. ret[i] = j;
  26. k = i;
  27. }
  28. }
  29. }

KMP&拓展KMP的更多相关文章

  1. hdu-4763(kmp+拓展kmp)

    题意:给你一个串,问你满足最大字串既是前后缀,也在字符串除去前后缀的位置中出现过: 思路:我用的是拓展kmp求的前后缀,只用kmp也能解,在字符串2/3的位置后开始遍历,如果用一个maxx保存前2/3 ...

  2. hdu-4300(kmp或者拓展kmp)

    题意:乱七八糟说了一大堆,就是先给你一个长度26的字符串,对应了abcd....xyz,这是一个密码表.然后给你一个字符串,这个字符串是不完整的(完整的应该是前半部分是加密的,后半部分是解密了的),然 ...

  3. poj-2752(拓展kmp)

    题意:求一个串所有的前后缀字串: 解题思路:kmp和拓展kmp都行,个人感觉拓展kmp更裸一点: 拓展kmp: #include<iostream> #include<algorit ...

  4. hdu 4333"Revolving Digits"(KMP求字符串最小循环节+拓展KMP)

    传送门 题意: 此题意很好理解,便不在此赘述: 题解: 解题思路:KMP求字符串最小循环节+拓展KMP ①首先,根据KMP求字符串最小循环节的算法求出字符串s的最小循环节的长度,记为 k: ②根据拓展 ...

  5. HDU 3613 Best Reward(拓展KMP算法求解)

    题目链接: https://cn.vjudge.net/problem/HDU-3613 After an uphill battle, General Li won a great victory. ...

  6. 拓展KMP算法详解

    拓展KMP解决的问题是给两个串S和T,长度分别是n和m,求S的每一个后缀子串与T的最长公共前缀分别是多少,记作extend数组,也就是说extend[i]表示S[i,n-1](i从0开始)和T的最长公 ...

  7. Period II FZU - 1901(拓展kmp)

    拓展kmp板题 emm...我比较懒 最后一个字母进了vector两个1  不想改了...就加了个去重... 哈哈 #include <iostream> #include <cst ...

  8. Simpsons’ Hidden Talents HDU - 2594(拓展kmp)

    Sample Input clinton homer riemann marjorie Sample Output 0 rie 3 看输出才题意...拓展kmp特征很明显嘛....注意开始就匹配到尾的 ...

  9. Seek the Name, Seek the Fame POJ - 2752(拓展kmp || kmp)

    题意: 就是求前缀和后缀相同的那个子串的长度  然后从小到大输出 解析: emm...网上都用kmp...我..用拓展kmp做的  这就是拓展kmp板题嘛... 求出extend数组后  把exten ...

随机推荐

  1. 【BZOJ5099】[POI2018]Pionek 几何+双指针

    [BZOJ5099][POI2018]Pionek Description 在无限大的二维平面的原点(0,0)放置着一个棋子.你有n条可用的移动指令,每条指令可以用一个二维整数向量表示.每条指令最多只 ...

  2. 【BZOJ4429】[Nwerc2015] Elementary Math小学数学 最大流

    [BZOJ4429][Nwerc2015] Elementary Math小学数学 Description Ellen给她的学生教小学数学.期末考试已经来临了.考试有n个题目,每一个题目学生们都要对一 ...

  3. SqlServer复杂存储过程

    SqlServer复杂存储过程 CREATE PROCEDURE FETCH_GOOUT_INFO AS BEGIN WITH l as(SELECT A.ZJHM, O.KSQR, O.JSRQ, ...

  4. innodb的innodb_buffer_pool_size和MyISAM的key_buffer_size(转自:http://www.java123.net/898181.html)

    一. key_buffer_size 对MyISAM表来说非常重要. 如果只是使用MyISAM表,可以把它设置为可用内存的 -%.合理的值取决于索引大小.数据量以及负载 -- 记住,MyISAM表会使 ...

  5. 【git】------git的基本命令------【巷子】

    001.初始化仓库,创建git仓库 git init 002.配置个人信息 git config --global user.name <名字> --------->:配置用户名 g ...

  6. 自定义Realm解析

    自定义Realm解析---------------------------------------> /* * Copyright 2005-2013 shopxx.net. All right ...

  7. CentOS7使用yum安装nginx

    CentOS默认没有nginx的yum源需要yum安装nginx可以使用一下方法 一,环境检测 二,设置yum源 rpm -Uvh http://nginx.org/packages/centos/7 ...

  8. qt——exec()的基本用法

    qt中 if(my1.exec()==QDialog::Accepted) 是什么意思 这个先说这个my1.exec()这个就是个等待消息的循环,就是说它在等待你给的命令. 再说这个QDialog:: ...

  9. WebMagic简介和使用

    概览 WebMagic是一款简单灵活的爬虫框架.基于它你可以很容易的编写一个爬虫. WebMagic项目代码分为核心和扩展两部分. 核心部分(webmagic-core)是一个精简的.模块化的爬虫实现 ...

  10. EasyUI Easyloader 加载器

    用法 加载 EasyUI 模块 easyloader.base = '../'; // 设置 easyui 的基本目录 easyloader.load('messager', function(){ ...