题目:假如要判断字符串A"ABA"是不是字符串B"ABABABA"的子串。

解法一:暴力破解法, 直接枚举所有的长度为3的子串,然后依次与A比较,这样就能得出匹配的位置。 这样的时间复杂度是O(M*N),M为B的长度,N为A的长度。

解法二:Rabin-Karp算法

  思想:假设待匹配字符串的长度为N,目标字符串的长度为M(M>N);首先计算待匹配字符串的hash值,计算目标字符串前N个字符的hash值;比较前面计算的两个hash值,比较次数M-N+1:若hash值不相等,则继续计算目标字符串的下一个长度为N的字符子串的hash值,若hash值相同,则需要使用比较字符是否相等再次判断是否为相同的子串(这里若hash值相同,则直接可以判断待匹配字符串是目标字符串的子串,之所以需要再次判断字符是否相等,是因为不同的字符计算出来的hash值有可能相等,称之为hash冲突或hash碰撞,不过这是极小的概率,可以忽略不计);

  哈希函数定义如下:

    其中Cm表示字符串中第m项所代表的特地数字,有很多种定义方法,我习惯于用java自带的char值,也就是ASCII码值。java中的char是16位的,用的Unicode编码,8位的ASCII码包含在Unicode中。b是哈希函数的基数,相当于把字符串看作是b进制数。h是防止哈希值溢出。

    

  代码:

 public class RabinKarp {

     public static void main(String[] args) {
String s = "ABABABA";
String p = "ABA";
match(p, s);
} /**
* @param p 模式
* @param s 源串
*/
static void match(String p,String s){
long hash_p = hash(p);//p的hash值
int p_len = p.length();
for (int i = 0; i+p_len<= s.length(); i++) {
long hash_i = hash(s.substring(i, i+p_len));// i 为起点,长度为p_len的子串的hash值
if (hash_p==hash_i) {
System.out.println("match:"+i);
}
}
} final static long seed = 31; // 进制数 /**
* 不同的字符计算出来的hash值相同 称为hash冲突
* 使用100000个不同字符串产生的冲突数,大概在0~3波动,使用100百万不同的字符串,冲突数大概110+范围波动。
* @param str
* @return
*/
private static long hash(String str) {
long h = 0;
for (int i = 0; i !=str.length(); i++) {
// 这个计算方式就是 An²+Bn+c 的循环表达式,而这个计算方式就是二进制转十进制的计算方式
// 这里n=31,可以理解为转为31进制
h = seed * h + str.charAt(i); }
return h%Long.MAX_VALUE; // 防止hash值过大
} }

  结果:

    

  在这里计算一下时间复杂度,计算hash值的时间为O(N),目标字符串长度为M,所以时间复杂度为O(M*N)。好像和暴力破解差不多。下面会通过一种类似于预处理的方式来进行优化,叫做滚动哈希。就是提前计算好源串的hash值,构建成一个hash数组,再通过比较hash值,这样就成功匹配出来了。通过这种优化,时间复杂度下降到O(M+N),O(N)为计算待匹配的字符串计算hash值的时间,O(M)为计算hash数组的时间。

  滚动哈希的技巧就是:如果已经算出从k到k+m的子串的哈希值H(S[k,k+1...k+m]),那么从k+1到k+m+1的子串的哈希值就可以基于前一个的哈希值计算得出。

    

  代码:

 /**
* 滚动哈希法
* 对目标字符串按d进制求值,mod h 取余作为其hash
* 对源串,一次求出m个字符的hash,保存在数组中(滚动计算)
* 匹配时,只需对比目标串的hash值和预存的源串的hash值表
*/
public class RabinKarp_1 { public static void main(String[] args) {
String s = "ABABABA";
String p = "ABA";
match(p, s);
} static void match(String p,String s){
long hash_p = hash(p);//p的hash值
long[] hashOfS = hash(s, p.length());
for (int i = 0; i < hashOfS.length; i++) {
if (hashOfS[i] == hash_p) {
System.out.println("match:" + i);
}
}
} final static long seed = 31; /**
* 滚动哈希
* @param s 源串
* @param n 子串的长度
* @return
*/
private static long[] hash(String s, int n) {
long[] res = new long[s.length() - n + 1];
//前n个字符的hash
res[0] = hash(s.substring(0, n));
for (int i = n; i < s.length(); i++) {
char newChar = s.charAt(i); // 新增的字符
char oldchar = s.charAt(i - n); // 前n字符的第一字符
//前n个字符的hash*seed-前n字符的第一字符*seed的n次方
long v = (long) ((res[i - n] * seed + newChar - Math.pow(seed, n) * oldchar) % Long.MAX_VALUE);
res[i - n + 1] = v;
}
return res;
} static long hash(String str) {
long h = 0;
for (int i = 0; i != str.length(); ++i) {
h = seed * h + str.charAt(i);
}
return h % Long.MAX_VALUE;
}
}

  结果:

     

字符串匹配(一)----Rabin-Karp算法的更多相关文章

  1. 模式字符串匹配问题(KMP算法)

    这两天又看了一遍<算法导论>上面的字符串匹配那一节,下面是实现的几个程序,可能有错误,仅供参考和交流. 关于详细的讲解,网上有很多,大多数算法及数据结构书中都应该有涉及,由于时间限制,在这 ...

  2. [小专题]另一种字符串匹配的思路——Shift-And算法

    吐槽:前两天打组队赛遇到一个字符串的题考了这个(见:http://acm.hdu.edu.cn/showproblem.php?pid=5972 ) 当时写了个KMP瞎搞然后TLE了(害),赛后去查了 ...

  3. 字符串匹配的Boyer-Moore(BM)算法

    各种文本编辑器的"查找"功能(Ctrl+F),大多采用Boyer-Moore算法. Boyer-Moore算法不仅效率高,而且构思巧妙,容易理解.1977年,德克萨斯大学的Robe ...

  4. 神奇的字符串匹配:扩展KMP算法

    引言 一个算是冷门的算法(在竞赛上),不过其算法思想值得深究. 前置知识 kmp的算法思想,具体可以参考 → Click here trie树(字典树). 正文 问题定义:给定两个字符串 S 和 T( ...

  5. 字符串匹配--Karp-Rabin算法

    主要特征 1.使用hash函数 2.预处理阶段时间复杂度O(m),常量空间 3.查找阶段时间复杂度O(mn) 4.期望运行时间:O(n+m) 本文地址:http://www.cnblogs.com/a ...

  6. 字符串匹配&Rabin-Karp算法讲解

    问题描述: Rabin-Karp的预处理时间是O(m),匹配时间O( ( n - m + 1 ) m )既然与朴素算法的匹配时间一样,而且还多了一些预处理时间,那为什么我们还要学习这个算法呢?虽然Ra ...

  7. 算法——字符串匹配Rabin-Karp算法

    前言 Rabin-Karp字符串匹配算法和前面介绍的<朴素字符串匹配算法>类似,也是相应每一个字符进行比較.不同的是Rabin-Karp採用了把字符进行预处理,也就是对每一个字符进行相应进 ...

  8. 字符串匹配的KMP算法

    ~~~摘录 来源:阮一峰~~~ 字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串”BBC ABCDAB ABCDABCDABDE”,我想知道,里面是否包含另一个字符串”ABCDABD”? 许 ...

  9. sdut 2125串结构练习--字符串匹配【两种KMP算法】

    串结构练习——字符串匹配 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目链接:http://acm.sdut.edu.cn/sduto ...

  10. 字符串匹配的KMP算法详解及C#实现

    字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD" ...

随机推荐

  1. 动态的加载显示oracle警告日志文件内容

    Last login: Fri Jan 25 00:37:47 2019 from oracle [root@oracle ~]# su - oracle [oracle@oracle ~]$ sql ...

  2. 前端开发,走浏览器缓存真的很烦,拒绝浏览器走缓存从meta标签做起!

    <meta http-equiv="Cache-Control" content="no-cache" /> <meta http-equiv ...

  3. Web前端-网站首页和注册界面的实现

    首页用到的知识如下: 1.bootstrap框架 2.jQuerry实现页面定时弹出广告 注册界面用到的知识: 1.bootstrap框架 2.jQuerry实现省市联动操作 3.jQuerry实现表 ...

  4. ES2018新特性(译文)

    原文链接:css-tricks.com 第9版ECMAScript标准于2018年6月发布,正式名称为ECMAScript 2018(简称ES2018).从ES2016开始,ECMAScript规范的 ...

  5. javaweb c3p0连接oracle12c

    最近在搞javaweb,在连接池上碰到了一系列的问题,在Junit测试时,oracle12c报错: ORA-28040: 没有匹配的验证协议 百度解决:修改 $ORACLE_HOME/network/ ...

  6. iOS开发之GCD同步主线程、异步主线程

    /** 在主线程执行block */ + (void)gs_synExecuteOnMainThread:(void (^)(void))block { if ((nil == block) || ( ...

  7. 饮冰三年-人工智能-Python-29瀑布流

    多适用于:整版以图片为主,大小不一的图片按照一定的规律排列的网页布局. 1:创建model类,并生成数据表 from django.db import models # Create your mod ...

  8. 【玩转开源】制作Docker镜像

    做嵌入式方向经常会遇到的一个问题,就是编译环境安装,如果换电脑,再重新安装环境是一个比较费时的事情,这个时候可以自己制作一个Docker镜像,然后把编译环境在Docker镜像里面配置好,以后同步环境就 ...

  9. How Classes are Found

    转载自: https://docs.oracle.com/javase/7/docs/technotes/tools/findingclasses.html How Classes are Found ...

  10. Linux中设置服务自启动的三种方式,ln -s 建立启动软连接

    有时候我们需要Linux系统在开机的时候自动加载某些脚本或系统服务(http://www.0830120.com) 主要用三种方式进行这一操作: ln -s 在/etc/rc.d/rc*.d目录中建立 ...