字符串匹配(一)----Rabin-Karp算法
题目:假如要判断字符串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算法的更多相关文章
- 模式字符串匹配问题(KMP算法)
这两天又看了一遍<算法导论>上面的字符串匹配那一节,下面是实现的几个程序,可能有错误,仅供参考和交流. 关于详细的讲解,网上有很多,大多数算法及数据结构书中都应该有涉及,由于时间限制,在这 ...
- [小专题]另一种字符串匹配的思路——Shift-And算法
吐槽:前两天打组队赛遇到一个字符串的题考了这个(见:http://acm.hdu.edu.cn/showproblem.php?pid=5972 ) 当时写了个KMP瞎搞然后TLE了(害),赛后去查了 ...
- 字符串匹配的Boyer-Moore(BM)算法
各种文本编辑器的"查找"功能(Ctrl+F),大多采用Boyer-Moore算法. Boyer-Moore算法不仅效率高,而且构思巧妙,容易理解.1977年,德克萨斯大学的Robe ...
- 神奇的字符串匹配:扩展KMP算法
引言 一个算是冷门的算法(在竞赛上),不过其算法思想值得深究. 前置知识 kmp的算法思想,具体可以参考 → Click here trie树(字典树). 正文 问题定义:给定两个字符串 S 和 T( ...
- 字符串匹配--Karp-Rabin算法
主要特征 1.使用hash函数 2.预处理阶段时间复杂度O(m),常量空间 3.查找阶段时间复杂度O(mn) 4.期望运行时间:O(n+m) 本文地址:http://www.cnblogs.com/a ...
- 字符串匹配&Rabin-Karp算法讲解
问题描述: Rabin-Karp的预处理时间是O(m),匹配时间O( ( n - m + 1 ) m )既然与朴素算法的匹配时间一样,而且还多了一些预处理时间,那为什么我们还要学习这个算法呢?虽然Ra ...
- 算法——字符串匹配Rabin-Karp算法
前言 Rabin-Karp字符串匹配算法和前面介绍的<朴素字符串匹配算法>类似,也是相应每一个字符进行比較.不同的是Rabin-Karp採用了把字符进行预处理,也就是对每一个字符进行相应进 ...
- 字符串匹配的KMP算法
~~~摘录 来源:阮一峰~~~ 字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串”BBC ABCDAB ABCDABCDABDE”,我想知道,里面是否包含另一个字符串”ABCDABD”? 许 ...
- sdut 2125串结构练习--字符串匹配【两种KMP算法】
串结构练习——字符串匹配 Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里^_^ 题目链接:http://acm.sdut.edu.cn/sduto ...
- 字符串匹配的KMP算法详解及C#实现
字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD" ...
随机推荐
- eclipse下properties文件中文乱码的解决方案
今天在工程下编辑.properties文件时输入了中文然后就保存出错,弄了好久才搞定!大家瞄瞄 在中文操作系统下,Eclipse中的Java类型文件的编码的默认设置是GBK,但是对Properties ...
- CISPA Scyther tools
1.Scyther软件作者网站的整理 Scyther工具的网站主页:https://people.cispa.io/cas.cremers/index.html 首先 对Scyther软件的资料进行整 ...
- Django-model
Model:数据库操作 创建数据库的表: django不能自动创建数据库,但能创建表 在web的models里定义生成表 在project的settings里设置app定义和数据库信息 步骤: 1.创 ...
- python爬虫之如何随机更换User-Agent
python爬虫爬取网站内容时,如果什么也没带,即不带报头headers,往往会被网站管理维护人员认定为机器爬虫.因为,此时python默认的user-agent如Python-urllib/2.1一 ...
- Centos安装Consul微服务
一.简介 Consul([ˈkɒnsl],康搜)是注册中心,服务提供者.服务消费者等都要注册到Consul中,这样就可以实现服务提供者.服务消费者的隔离.除了Consul之外,还有Eureka.Zoo ...
- systemd-unit
参考: systemd unit 中文手册
- Python语言的PyCharm开发工具的简单使用
1.创建一个新的项目 创建一个目录或者文件夹 设置模板:制作一个常用的,模板 # @Author:lnx# @version V1.0# -*- coding:UTF-8 -*- 搜索/替换快捷键 代 ...
- 2.DNN-神经网络推导
DNN就是我们常常说的深度神经网络,可以说由其衍生出来的各种深度算法都在AI界大行其道,今天就好好理一下这个算法.参考的是刘建平老师的博客:http://www.cnblogs.com/pinard/ ...
- Centos发布java的war包后,无法访问发布的工程
分析: 这时候,看你的开启的端口,8080,3306都能访问的话,那么就是你的地址写错了,可是,经测试,c3p0中的web地址是正确的,那么就去看linux中的tomcat的日志文件再tomcat/l ...
- SQL Server 分区表上建立ColumnStore Index 如何添加新分区方法与步骤
在生产环境中会遇到这样的场景,一个表随着时间的推移,越来越大,这个时候我们开始动手为这个表建立分区来改进查询性能. 但是表过大上百个G的时候,在数据仓库中,为了改进查询性能,我们可以添加在分区表的基础 ...