题意:
  • 任意给定一段字符串str(“123abc123abc00abc”)
  • 再输入一个关键字key(“abc”)
  • 要求返回str中包含key的所有子串的头下标
解法1(暴力法)

思路:

  • 以key串长度进行窗口滑动
  • str中[startIndex, endIndex]的子串与key相同则匹配

复杂度:窗口在n长度的母串滑动复杂度为O(n),每次比较m长度子串与key串的复杂度为O(m),综合来看暴力法复杂度为O(n*m)

    public static ArrayList<Integer> match(String str, String key) {ArrayList<Integer> list = new ArrayList<>();for(int startIndex = 0; startIndex < str.length(); startIndex++) {int endIndex;if((endIndex = startIndex + key.length() -1) > str.length() - 1) break;if(str.substring(startIndex,endIndex + 1).equals(key)) {list.add(startIndex);}}return list;}
解法2(Rabin-Karp算法 ----- 哈希法)

思路:

  • 以key串长度进行窗口滑动
  • 使用哈希算法计算出key串的哈希值
  • str中[startIndex, endIndex]的子串的哈希值与key串哈希值相同则匹配

复杂度:窗口在n长度的母串滑动复杂度为O(n),每次比较m长度子串的哈希值与key串哈希值复杂度为O(1),每次计算m长度子串的哈希值与key串哈希值复杂度为O(m),综合来看该方法复杂度为O(n*m)

 private static final int seed = 31;public static ArrayList<Integer> match2(String str, String key) {ArrayList<Integer> list = new ArrayList<>();long hashOfKey = hash(key);for (int startIndex = 0; startIndex < str.length(); startIndex++) {int endIndex;if ((endIndex = startIndex + key.length() - 1) > str.length() - 1) break;if (hash(str.substring(startIndex, endIndex + 1)) == hashOfKey) list.add(startIndex);}return list;}// 哈希算法public static long hash(String str) {int hash = 0;for (int i = 0; i < str.length(); i++) {hash = hash * seed + str.charAt(i);}return hash % Long.MAX_VALUE;}

关于哈希算法作用、原理和哈希冲突:

  1. 作用:能够唯一表示一个字符串,相同的字符串其哈希值相同,不相同的字符串哈希值一定不同
  2. 原理:"abc"字符串的哈希值为(seed2∗a+seed1∗a+seed0∗a)(seed^2 * a + seed^1 * a + seed^0 * a)(seed2∗a+seed1∗a+seed0∗a)% Long.MAX_VALUE,可以理解成一个递推式:an+1=seed∗an+srt(n)a_{n+1} = seed*a_n + srt(n)an+1​=seed∗an​+srt(n)(n=0,1,2,3…),当n=0时an=0a_n=0an​=0
  3. 哈希冲突:哈希冲突的意思就是,使用该哈希算法后,可能会出现不同字符串的哈希值也相同,这是有一定概率会出现的误差,本哈希算法计算100万个字符串的哈希值,可能会出现110个左右的冲突数

优化:

  • 使用滚动哈希法计算出母串中所有key串长度的子串哈希值,保存在一个数组中

  • 滚动哈希的原理:

    1. 先计算出第一个窗口长度的子串哈希值,复杂度为O(m)
    2. 利用公式:本窗口子串哈希值=上一个窗口子串的哈希值 * seed + 本窗口最后一个字符 - 上一个窗口第一个字符*pow(seed,lengthOfKey),例如(C0∗seed2+C1∗seed1+C2∗seed0)(C_0*seed^2+C_1*seed^1+C_2*seed^0)(C0​∗seed2+C1​∗seed1+C2​∗seed0)是第一个窗口的哈希值,那么第二个窗口的哈希值就为(第一个窗口哈希值 * seed)−C0∗seed3-C_0*seed^3−C0​∗seed3,该循环复杂度为O(n)
    3. 综上上滚动哈希的复杂度为O(m+n)
  • 最后进行窗口滑动把key串哈希值与保存了所有子串哈希值的数组进行匹配

    public static ArrayList<Integer> match3(String str, String key) {ArrayList<Integer> list = new ArrayList<>();// 滚动哈希(O(m+n))long[] hashes = hashes(str, key.length());// 求key串哈希值(O(m))int hashOfKey = hash(key);// 窗口扫描匹配哈希值(O(n))for (int startIndex = 0; startIndex < str.length(); startIndex++) {int endIndex;if ((endIndex = startIndex + key.length() - 1) > str.length() - 1) break;if (hashOfKey == hashes[startIndex]) list.add(startIndex);}return list;}public static long[] hashes(String str, int lengthOfKey) {long[] hashes = new long[str.length() - lengthOfKey + 1];hashes[0] = hash(str.substring(0, lengthOfKey));for (int startIndex = 1; startIndex < str.length(); startIndex++) {int endIndex;if ((endIndex = startIndex + lengthOfKey - 1) > str.length() - 1) break;// 滚动哈希算法:本窗口子串哈希值=上一个窗口子串的哈希值 * seed + 本窗口最后一个字符 - 上一个窗口第一个字符*pow(seed,lengthOfKey)hashes[startIndex] = hashes[startIndex - 1] * seed + str.charAt(endIndex) - str.charAt(startIndex - 1) * (long) Math.pow(seed, lengthOfKey);}return hashes;}public static long hash(String str) {int hash = 0;for (int i = 0; i < str.length(); i++) {hash = hash * seed + str.charAt(i);}return hash % Long.MAX_VALUE;}
收获:
  • 滑动窗口的startIndex与保存了所有子串哈希值的数组下标同步
  • 滚动哈希的核心思想:本窗口子串哈希值=上一个窗口子串的哈希值 * seed + 本窗口最后一个字符 - 上一个窗口第一个字符*pow(seed,lengthOfKey),可以自己举例子推导出来

字符串匹配问题 ----- Rabin-Karp算法相关推荐

  1. Rabin Karp 算法详解及Python实现

    目录 一.Rabin Karp 核心思路 二.字符串如何做哈希映射 三.借助前缀和列表计算滑动窗口 四.leetcode28. 代码实现 Rabin Karp 算法是用于实现字符串的模式匹配,先看le ...

  2. C++Rabin Karp算法字符串快速查找(附完整源码)

    C++Rabin Karp算法字符串快速查找 C++Rabin Karp算法字符串快速查找完整源码(定义,实现,main函数测试) C++Rabin Karp算法字符串快速查找完整源码(定义,实现,m ...

  3. 数据结构与算法 / 字符串匹配 / BF、PK 算法

    零.前言 为了下面便于说明,先定义两个名词,分别是主串和模式串.在字符串 A 中查找字符串 B,则 A 为主串,B 为模式串. 假设,主串中字符数量为 L1,模式串的字符数量为 L2 . 一.BF 算 ...

  4. leetcode 1044. Longest Duplicate Substring | 1044. 最长重复子串(Rabin Karp算法)

    题目 https://leetcode.com/problems/longest-duplicate-substring/ 题解 这题暴力超时,看了 Related Topics,以及 Hint,主要 ...

  5. 模糊字符串匹配:双音素算法

    介绍 名称匹配的主要问题之一是出错的可能性. 人们拼写同一个名字(错别字)的方式有很多,他们都听别人说的话. 有多种方法可以破坏自由格式语言数据. 当您需要搜索/匹配不良数据时,这会引起很多麻烦. 有 ...

  6. 字符串匹配的三种算法

    1.一道字符串匹配题: 实现 strStr() 函数. 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0 ...

  7. 【算法无用系列】字符串匹配那些事——BM算法

    文章目录 前言 一.BM算法 1.1.坏字符规则 1.2.好后缀规则 前言 BF算法和RK算法思路比较简单,但是效率却不尽人意,适合较短的字符串匹配时使用,如果需要在较长的字符串匹配时,则需在算法上进 ...

  8. 模糊字符串匹配:双重解密算法

    名称匹配的一个大问题是错误的倾向.有许多不同的方式,人们拼写相同的名字,打字错误,误读了另一个人说的话.有许多方法可以免费形式的语言数据被破坏.当您需要搜索/匹配不良数据时,会导致许多头疼. 有很多不 ...

  9. 字符串匹配之RabinKarp《算法很美》

    字符串匹配之RabinKarp 给定一个母串,再给定一个子串,要在母串中找是否有与子串对应的字符串 例如:母串S:ABABAC 子串P:BAB 思路: 滚动hash法,什么是滚动呢?哈哈,滚动的意思有 ...

  10. 字符串匹配rk算法c语言,字符串匹配问题(BFRK算法)

    1. 题目 有一个主串S={a,b,c,a,c,a,b,d,c},模式串T={a,b,d},请找出模式串在主串中第一次出现的位置 提示:不需要考虑字符串大小写问题,字符均为小写字母 2. BF算法 B ...

最新文章

  1. [你必须知道的.NET]第九回:品味类型---值类型与引用类型(中)-规则无边
  2. zabbix监控搭建
  3. 后端学习 - SpringMVC
  4. Java开发人员访谈的MindMap
  5. java实现线程的方式_java多线程实现的四种方式
  6. android仿高德地图透明黑字,Android 仿高德地图可拉伸的BottomSheet
  7. linux redis 启动用户,redis 进程使用root用户启动 -- 整改方案
  8. 【Kafka】Kafka使用代码设置offset值
  9. Python使用超高效算法查找所有类似123-45-67+89=100的组合
  10. 浅谈几种常见 RAID 的异同
  11. java oracle11g jar_oracle11g驱动jar包下载
  12. Django中文文档2.0
  13. 自然图像中的logo识别和定位:Logo localization andrecognition in natural images using homographic class graphs
  14. 毕设论文-word格式问题
  15. linux环境下,mysql数据库的完美卸载!(亲测)
  16. 我的世界服务器怎么改无限力量,我的世界无限力量效果指令,我的世界怎么用命令方块做无限的效果...
  17. 微习惯--简单到四个
  18. 仿微信表情输入键盘(支持 Gif 表情图文混排 )
  19. iphone11官网HTML,苹果小白入手iPhone11,以下几个基础操作要知道
  20. 嵌入式ARM设计编程(二) 字符串拷贝

热门文章

  1. 落花美眷,终究抵不过逝水流连,回忆我的2016,展望2017。
  2. B2B多商铺初期权限数据库设计
  3. Linux下C++静态库、动态库的制作与使用
  4. 鼠标指向表格时 显示更多信息 toolTipController1
  5. 一步一坑学android之禁用Appt2(andriod studio3.0)
  6. 期货市场技术分析01_理论基础
  7. 各种机器学习的应用场景分别是什么
  8. HMM隐马尔可夫模型(HMM)攻略
  9. Web报表页面如何传递中文参数
  10. google map flex