- 浏览: 253357 次
- 性别:
- 来自: 南京
文章分类
最新评论
-
mabusyao:
漠北空城 写道请问下,你这个是JDK版本是多少呢?!忘记了,应 ...
HashMap 源码解读 -
漠北空城:
请问下,你这个是JDK版本是多少呢?!
HashMap 源码解读 -
schumee:
完美团队~
项目沉思录 - 1.1 -
winie:
整理下 搞成引擎嘛 国产需要这样的engine
简单工作流引擎 -
mabusyao:
某位同学给我提供的堪称完美的解决方案:1. 将三个int数组放 ...
CraneWork
因为在网上搜寻hash算法的知识,无意中又找到一些字符串搜索算法。 由于之前已经学习过一些搜索算法,觉得应该可以归为一类。因此就写一篇文章来记录下学习的过程。
问题:
在一长字符串中找出其是否包含某子字符串。
首先当然还是简单算法,通过遍历来检索所有的可能:
时间复杂度为 Θ((n-m+1) m)
Rabin–Karp,即hash检索法:
这个算法的核心思想是,通过hash值,我们可以一次匹配一整条字串,速度上要快很多。
关键: 选择这样一种hash算法,使得从前一个hash值到后一个hash值仅需要常量的步骤。
我这里实现的hash算法可以做到这点,但是有效性并不高,应该还有其他的hash算法可以更好了减少冲突的发生。
KMP算法
KMP算法说简单也不简单,说复杂也不复杂。只要你理解了它的核心思想,代码量其实非常少。可是想要解释它的思想,却也不是一件容易的事情。
考虑再三,还是觉得自己无法胜任这个解释工作,于是找了一篇自己认为解释KMP算法比较透彻的文章,翻译出来,看看大家有没有更哈的建议。
KMP algorithm
http://en.wikipedia.org/wiki/Knuth-Morris-Pratt_algorithm
实例
为了解释该算法的细节,我们首先利用一个实例来把算法的步骤过一遍。在这个过程中的任意时间点,该算法的状态都由两个变量来决定, m和i。 m代表在S中,某个对于W(pattern)匹配的起始位置。 i代表在W中的当前正在进行匹配工作的位置。我们来描述一下当算法开始时的状态:
1 2
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W: ABCDABD
i: 0123456
我们首先从0位开始匹配W和S中平行的字符串,如果匹配,则前进到下一位。然而当我们到第四步的时候,我们发现S[3]是空格而W[3]=‘D’,出现了第一个不匹配。在传统的模式匹配算法中,接下来我们应该从S[1]的位置重新开始匹配。然而,如果我们仔细观察,在S的0到3位中(也就是我们刚刚进行过匹配成功的位),除了0位,其它都没有‘A‘出现过。而假设某字符串中有和W匹配的子串,那么这个子串必须是以’A‘开头的。因此,我们可以确定,S的0到3位中,都不可能存在这样的子串,于是我们决定从S的4位中重新找起。也就是说,m=4, i=0.
1 2
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W: ABCDABD
i: 0123456
此时,我们获得了一个几乎匹配的“ABCDAB”,然后在W[6](S[10])这个位置上,我们又出现了一个不匹配。于是,我们应该继续扩大m的值去寻找下一个可能的匹配。那么m的下一个值应该设置为多少呢?
整个算法的核心就在于此,我们可以从不匹配的位置开始,即m=10,然后这并不是一个正确的选择,我们发先在S的4到10位中,第8位也是‘A’。因此,如果我们从第十位开始继续找起的话,有可能就错过了某个匹配。
S中第八位的‘A’和第九位的‘B’,分别跟W的第0第1位相匹配。KMP算法对此的处理是,新的m=8(即首位匹配的值),新的i=2(因为前两位根据统计,已经是匹配好的了)。然后我们从S的m+i开始匹配W的i位。
1 2
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W: ABCDABD
i: 0123456
跟第一步类似,我们在i=2就出错了,下一步我们应该跳转到哪里呢?当然是m=11,i=0:
1 2
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W: ABCDABD
i: 0123456
此时,我们又在m=17的位置上出错了,根据第二步的解释,我们这次跳到m=15而i=2:
1 2
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W: ABCDABD
i: 0123456
找到该模式,算法结束,返回m的值15.
部分匹配表
如果我们刚才的分析那样,整个匹配算法的核心就在于,当某次匹配过程出现不匹配的值时,如何寻找下一个做匹配的位置(这里的位置包括两个概念,即起始位置和我们应该从哪个值开始做匹配)。这样的值当然是越大越好,因为选择的下一个匹配位置越大,我们跳过的值就越多,整个算法就越快。
如果单看我们之前的分析,好像这个确定下一个匹配位置的工作关系到S和W两张表。其实不是这样的,我们只需要对W进行一个预处理,就可以做到这点。
还是来看,当W是“ABCDABD”这样一个字串时。我们会发现除了起始位置是‘A’以外,4位的值也是‘A’,那么如果我们在某次匹配时,匹配到了W的第4位,那么下一次做匹配查找时,就应当从W第4位对应的那个字符开始:
m 123456
S ... ABCDAX...
W ABCDABD
i 0123456
因为我们已经知道S[5]和W[4]是匹配的了,那么其实就不需要再匹配一次了。因此匹配的起始位置是m=5,但是应当从i=1那里开始进行匹配。
如果是这样的一个情况呢?
m 1234567
S ... ABCDABX..
W ABCDABD
i 0123456
同样的道理,匹配的起始位置依然是m=5,但是应当从i=2开始匹配。
下面给出求出当从任一位出现不匹配是,应该从哪里开始从新匹配的算法:
既然已经得到了这样一个表,那么写出整个KMP算法也不是什么难事了:
最后两个算法分别是BM算法和有限自动机算法。昨天我花了一天的时间研究BM算法,对算法的本质有了一定的了解,但是对于如何编码还是有点困惑。
决定把这两个算法先放一下,在字符搜索算法上停留的时间有点长。还是等以后再继续学习。
问题:
在一长字符串中找出其是否包含某子字符串。
首先当然还是简单算法,通过遍历来检索所有的可能:
public static int naiveSearch(String content, String sub) { for(int i = 0; i < (content.length() - sub.length() + 1); i++) { boolean found = true; for(int j = 0 ; j < sub.length(); j++) { if(content.charAt(i + j) != sub.charAt(j)) { found = false; break; } } if(found) return i; } return -1; }
时间复杂度为 Θ((n-m+1) m)
Rabin–Karp,即hash检索法:
public static int rabinKarp(String content, String sub) { long hcontent = rshash(content.substring(0, sub.length())); long hsub = rshash(sub); for(int i = 0; i < (content.length() - sub.length()); i++) { //hcontent = rshash(content.substring(i, sub.length() + i)); if(hsub == hcontent) { if(sub.equals(content.substring(i, i + sub.length()))) { return i; } } hcontent = newhash(content, hcontent, i + 1, sub.length()); } return -1; } private static long rshash(String str) { int a = 63689; long hash = 0; for(int i = 0; i < str.length(); i++) { hash += a * str.charAt(i); } return hash; } private static long newhash(String str, long previous, int i, int length) { int a = 63689; long minHash = str.charAt(i - 1) * a; long plusHash = str.charAt(i + length - 1) * a; return (previous - minHash + plusHash); }
这个算法的核心思想是,通过hash值,我们可以一次匹配一整条字串,速度上要快很多。
关键: 选择这样一种hash算法,使得从前一个hash值到后一个hash值仅需要常量的步骤。
我这里实现的hash算法可以做到这点,但是有效性并不高,应该还有其他的hash算法可以更好了减少冲突的发生。
KMP算法
KMP算法说简单也不简单,说复杂也不复杂。只要你理解了它的核心思想,代码量其实非常少。可是想要解释它的思想,却也不是一件容易的事情。
考虑再三,还是觉得自己无法胜任这个解释工作,于是找了一篇自己认为解释KMP算法比较透彻的文章,翻译出来,看看大家有没有更哈的建议。
KMP algorithm
http://en.wikipedia.org/wiki/Knuth-Morris-Pratt_algorithm
实例
为了解释该算法的细节,我们首先利用一个实例来把算法的步骤过一遍。在这个过程中的任意时间点,该算法的状态都由两个变量来决定, m和i。 m代表在S中,某个对于W(pattern)匹配的起始位置。 i代表在W中的当前正在进行匹配工作的位置。我们来描述一下当算法开始时的状态:
1 2
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W: ABCDABD
i: 0123456
我们首先从0位开始匹配W和S中平行的字符串,如果匹配,则前进到下一位。然而当我们到第四步的时候,我们发现S[3]是空格而W[3]=‘D’,出现了第一个不匹配。在传统的模式匹配算法中,接下来我们应该从S[1]的位置重新开始匹配。然而,如果我们仔细观察,在S的0到3位中(也就是我们刚刚进行过匹配成功的位),除了0位,其它都没有‘A‘出现过。而假设某字符串中有和W匹配的子串,那么这个子串必须是以’A‘开头的。因此,我们可以确定,S的0到3位中,都不可能存在这样的子串,于是我们决定从S的4位中重新找起。也就是说,m=4, i=0.
1 2
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W: ABCDABD
i: 0123456
此时,我们获得了一个几乎匹配的“ABCDAB”,然后在W[6](S[10])这个位置上,我们又出现了一个不匹配。于是,我们应该继续扩大m的值去寻找下一个可能的匹配。那么m的下一个值应该设置为多少呢?
整个算法的核心就在于此,我们可以从不匹配的位置开始,即m=10,然后这并不是一个正确的选择,我们发先在S的4到10位中,第8位也是‘A’。因此,如果我们从第十位开始继续找起的话,有可能就错过了某个匹配。
S中第八位的‘A’和第九位的‘B’,分别跟W的第0第1位相匹配。KMP算法对此的处理是,新的m=8(即首位匹配的值),新的i=2(因为前两位根据统计,已经是匹配好的了)。然后我们从S的m+i开始匹配W的i位。
1 2
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W: ABCDABD
i: 0123456
跟第一步类似,我们在i=2就出错了,下一步我们应该跳转到哪里呢?当然是m=11,i=0:
1 2
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W: ABCDABD
i: 0123456
此时,我们又在m=17的位置上出错了,根据第二步的解释,我们这次跳到m=15而i=2:
1 2
m: 01234567890123456789012
S: ABC ABCDAB ABCDABCDABDE
W: ABCDABD
i: 0123456
找到该模式,算法结束,返回m的值15.
部分匹配表
如果我们刚才的分析那样,整个匹配算法的核心就在于,当某次匹配过程出现不匹配的值时,如何寻找下一个做匹配的位置(这里的位置包括两个概念,即起始位置和我们应该从哪个值开始做匹配)。这样的值当然是越大越好,因为选择的下一个匹配位置越大,我们跳过的值就越多,整个算法就越快。
如果单看我们之前的分析,好像这个确定下一个匹配位置的工作关系到S和W两张表。其实不是这样的,我们只需要对W进行一个预处理,就可以做到这点。
还是来看,当W是“ABCDABD”这样一个字串时。我们会发现除了起始位置是‘A’以外,4位的值也是‘A’,那么如果我们在某次匹配时,匹配到了W的第4位,那么下一次做匹配查找时,就应当从W第4位对应的那个字符开始:
m 123456
S ... ABCDAX...
W ABCDABD
i 0123456
因为我们已经知道S[5]和W[4]是匹配的了,那么其实就不需要再匹配一次了。因此匹配的起始位置是m=5,但是应当从i=1那里开始进行匹配。
如果是这样的一个情况呢?
m 1234567
S ... ABCDABX..
W ABCDABD
i 0123456
同样的道理,匹配的起始位置依然是m=5,但是应当从i=2开始匹配。
下面给出求出当从任一位出现不匹配是,应该从哪里开始从新匹配的算法:
private static void next(char[] input, int[] table) { int pos = 2; int cnd = 0; table[0] = -1; table[1] = 0; while(pos < input.length) { if(input[pos - 1] == input[cnd]) { table[pos] = cnd + 1; pos++; cnd++; } else if(cnd > 0) { cnd = table[cnd]; } else { table[pos] = 0; pos++; } } }
既然已经得到了这样一个表,那么写出整个KMP算法也不是什么难事了:
private static void next(char[] input, int[] table) { int pos = 2; int cnd = 0; table[0] = -1; table[1] = 0; while(pos < input.length) { if(input[pos - 1] == input[cnd]) { table[pos] = cnd + 1; pos++; cnd++; } else if(cnd > 0) { cnd = table[cnd]; } else { table[pos] = 0; pos++; } } }
最后两个算法分别是BM算法和有限自动机算法。昨天我花了一天的时间研究BM算法,对算法的本质有了一定的了解,但是对于如何编码还是有点困惑。
决定把这两个算法先放一下,在字符搜索算法上停留的时间有点长。还是等以后再继续学习。
发表评论
-
大数据下的实体解析
2016-07-07 12:03 671大数据时代的实体解析困境 <!--[if !sup ... -
中文相似度匹配算法
2015-12-30 14:44 1921基于音形码的中文字 ... -
各种语言写的wordcount
2015-09-24 16:07 0Java版本: String input ... -
数组双指针算法的研究
2015-07-14 16:59 2461双指针算法在数组/链 ... -
摩尔投票法
2015-06-30 20:13 18413摩尔投票法 提问: 给定一个int型数组,找出该数 ... -
(转)最长回文字串算法
2015-01-18 14:30 1438来自(http://blog.163.com/zhaohai ... -
STRUTS2 源码 - Logging System
2012-05-24 08:51 1405看了STRUTS2的源码,了解了它的logging系统,觉得还 ... -
在线词典的数据结构实现。
2012-05-18 08:37 0昨天在网上看到了一道百度的面试题: Baidu写道 ... -
存储中间计算结果的动态规划算法
2012-04-18 15:50 1218public class RodCutting { ... -
java写的四则运算器
2011-08-19 22:19 2724本打算做一个从RE到NFA的转换器,思路已经理清了,但是在动手 ... -
什么是最小二乘拟合
2007-09-14 21:27 963对于一个数据点(x1, y1), ... (xn, yn)的集 ... -
palindrome
2008-07-14 17:39 985package palindrome;/*Problem St ... -
CraneWork
2008-07-14 19:14 1033/*Problem Statement There are ... -
SalesRouting
2008-07-15 10:53 798package salesRouting;/*Problem ... -
FactorialSystem
2008-07-21 19:36 856/*Problem StatementIn the facto ... -
RecurringNumbers
2008-07-22 09:41 916/*Problem StatementA rational n ... -
HardDuplicateRemover
2008-07-23 15:17 935/*Problem StatementWe have a se ... -
BlockStructure
2008-07-23 20:55 800/*Problem StatementA group of v ... -
SkipStones
2008-07-28 17:31 826/*Problem StatementWhen a stone ... -
TheaterVisit
2008-07-28 17:31 784/*Problem StatementYou want to ...
相关推荐
字符串匹配是计算机科学中的一种基本问题,涉及到对文本数据的搜索和分析。在这个问题中,目标是找出一个较长的字符串(匹配串)中是否存在一个较短的子串(模式串)。这里我们将深入探讨几种常见的字符串匹配算法,...
Pratt三位学者于1977年提出的,它是一种高效的字符串搜索算法,主要用于在一个主串(text)中查找是否存在一个模式串(pattern)。KMP算法避免了在匹配过程中不必要的字符比较,显著提高了搜索效率,尤其当模式串中...
### 字符串匹配算法小集解析 #### 一、引言 本文档提供了一系列共35种不同的字符串匹配算法,这些算法在英文环境下被广泛讨论和应用。通过对这些算法进行详细的解析,我们可以更好地理解每种算法的特点、应用场景...
在实际应用中,如搜索引擎的日志处理、大规模文件的关键词提取、高频率词汇的统计等,字符串压缩算法起着至关重要的作用。通过散列将字符串映射到固定大小的桶中,可以有效地将大规模问题转化为小规模问题,从而利用...
在IT领域,字符串模式匹配是数据结构和算法中一个至关重要的主题,特别是在计算机病毒检测、文本搜索、生物信息学等领域有着广泛的应用。本实验“基于字符串模式匹配算法的病毒感染检测问题”聚焦于如何利用这些算法...
### 单字符串匹配算法总结 #### 一、引言 字符串匹配是计算机科学中的一个重要问题,涉及在一段文本(通常称为“主串”或“文本串”)中查找特定的子串(通常称为“模式串”)。这类问题广泛应用于文本搜索、数据...
字符串匹配是计算机科学中的一个重要领域,它涉及到在主文本中查找一个特定模式的过程。这个主题在文本处理、搜索引擎、数据挖掘以及许多其他应用中都至关重要。在这个话题中,我们将深入探讨几种经典的字符串匹配...
传统的字符串匹配算法如Knuth-Morris-Pratt(KMP)和Boyer-Moore(BM)家族,主要聚焦于在文本中快速查找精确模式,但随着应用场景的多样化,对算法灵活性的需求日益增长。BNDM算法正是在这种背景下应运而生,它旨在...
总结来说,"多文本内字符串查找程序"是一个利用C#实现的高效文本搜索工具,可能运用了先进的字符串查找算法,并结合了文件I/O操作和用户界面设计,以满足用户在大量文本数据中快速查找特定字符串的需求。
总结起来,Levenshtein算法在Delphi中的实现,不仅展示了字符串处理技术,还体现了动态规划的思想。通过理解和应用这个算法,开发者可以更好地处理文本数据,提升程序的智能化水平。而提供的压缩包文件则提供了一个...
Sunday算法是一种高效的子字符串搜索算法,它比经典的Boyer-Moore算法在某些情况下表现更优。该算法由Daniel Sunday提出,并在实际应用中得到了广泛的采纳。 ### Sunday算法的基本思想 Sunday算法的基本思想是通过...
根据给定的文件信息,本文将详细介绍如何使用Java实现基于字符串的余弦相似度算法,并应用于推荐系统中。 ### 一、引言 在推荐系统领域,为了衡量两个字符串之间的相似性,通常会采用多种算法,其中余弦相似度算法...
### 字符串匹配算法概述 #### 1. 引言 在信息学竞赛与实际应用领域中,字符串处理占据着极其重要的地位。虽然表面上看似简单的字符串处理问题,却蕴含着丰富的算法思想和技术挑战。本篇文章将详细介绍由ACM金牌...
KMP(Knuth-Morris-Pratt)算法是一种高效地在主串(text)中查找子串(pattern)的字符串模式匹配算法,由Dijkstra、Morris和Pratt在1970年提出。这个算法避免了不必要的字符比较,减少了回溯次数,提高了搜索效率...
Morris和Vaughan Pratt共同提出的,它是一种高效的字符串匹配算法,能够显著减少不必要的比较次数,从而提高搜索效率。KMP算法的核心在于构建一个“失败函数”,通过这个函数可以避免重复检查已经匹配过的部分,...
总结一下,字符串搜索是计算机科学中的基础内容,而C++作为通用编程语言,提供了实现这些算法的强大支持。通过学习和实践KMP、Boyer-Moore以及暴力搜索算法,我们可以更好地理解和解决字符串匹配的问题,提高代码...
总结起来,朴素的字符串匹配算法(BF算法)是字符串处理中最基础的单模式匹配方法,虽然其效率较低,但在理解和实现上具有直观性和简洁性。随着技术的发展,人们不断探索和改进,衍生出更多高效算法,以满足各种复杂...
根据提供的信息,《PDF电子书《柔性字符串匹配》》主要探讨了与字符串匹配相关的算法知识。字符串匹配是计算机科学中的一个核心问题,在文本处理、搜索引擎、生物信息学等多个领域都有着广泛的应用。下面将从不同的...
`IndexOf()`和`LastIndexOf()`方法用于查找字符或字符串首次出现的位置。前者从字符串开头开始搜索,而后者从结尾开始。如果没有找到匹配项,这两个方法都会返回-1。此外,`Contains()`方法可以用来检查一个字符串...
在技术实现上,字符串查找算法通常是基于不同的搜索策略,如朴素匹配、KMP算法、Boyer-Moore算法或Rabin-Karp算法。这些算法优化了搜索过程,使得在大型文件中查找指定字符串的速度大大提高。替换操作则涉及到字符串...