Go语言源码中的Rabin-Karp算法

strings.go包实现了一个Rabin-Karp算法.有点意思.

关于这个算法:

图灵社区的有一篇: 图说Rabin-Karp字符串查找算法

关于Go源码实现:

网友GoLove已写一个篇非常详细的说明了. http://www.cnblogs.com/golove/p/3234673.html

GoLove那个已经分析的非常清楚了,只是前面那一串说明太长了.我把他的说明替换成代码形式.

直接跑起来,这样更能看得清楚些.

package main 

import (
	"fmt"
    "unicode/utf8"
)

func main(){
	count := Count("9876520210520","520")
	fmt.Println("count==",count)
}

// primeRK is the prime base used in Rabin-Karp algorithm.
//primeRK相当于进制
//本例中,只用到0-9这10个数字,即所有字符的总个数为10,所以定为10
//源码中是16777619,即相当于16777619进制
//The magic is in the interesting relationship between the special prime
 //16777619 (2^24 + 403) and 2^32 and 2^8.
const primeRK = 10 // 16777619 

// hashStr returns the hash and the appropriate multiplicative
// factor for use in Rabin-Karp algorithm.
func hashStr(sep string) (uint32, uint32) {
	hash := uint32(0)
	charcode := [...]uint32{5,2,0} 

	for i := 0; i < len(sep); i++ {
		//hash = hash*primeRK + uint32(sep[i])
		hash = hash*primeRK + charcode[i]
	}

	//即相当于千位->百位->十位,得到乘数因子(pow),本例中的520,得到的pow是1000
	var pow, sq uint32 = 1, primeRK
	for i := len(sep); i > 0; i >>= 1 { //len(sep)=3 i>>{1,0} sq:{10,100}
		if i&1 != 0 {
			pow *= sq
		}
		sq *= sq
	}
	/*
	var pow uint32 = 1
	for i := len(sep); i > 0; i-- {
		pow *= primeRK
	}
	*/
	fmt.Println("hashStr() sep:",sep," hash:",hash," pow:",pow)
	return hash, pow
}

// Count counts the number of non-overlapping instances of sep in s.
func Count(s, sep string) int {
	fmt.Println("Count() s:",s," sep:",sep)

	n := 0
	// special cases
	switch {
	case len(sep) == 0:	//seq为空,返回总数加1
		return utf8.RuneCountInString(s) + 1
	case len(sep) == 1:	//seq为单个字符,直接遍历比较即可
		// special case worth making fast
		c := sep[0]
		for i := 0; i < len(s); i++ {
			if s[i] == c {
				n++
			}
		}
		return n
	case len(sep) > len(s):
		return 0
	case len(sep) == len(s):
		if sep == s {
			return 1
		}
		return 0
	}
	// Rabin-Karp search
	hashsep, pow := hashStr(sep) 

	lastmatch := 0 //最后一次匹配的位置
	charcode := [...]uint32{9,8,7,6,5,2,0,2,1,0,5,2,0} //对应字符串"9876520210520"

	//验证s字符串 0 - len(sep)是不是匹配的
	h := uint32(0)
	for i := 0; i < len(sep); i++ {
		//h = h*primeRK + uint32(s[i])
		h = h*primeRK +  charcode[i]
	}

	//如初始s的len(seq)内容是匹配的,n++, lastmatch指向len(seq)位置
	if h == hashsep && s[:len(sep)] == sep {
		n++
		lastmatch = len(sep)
	}

	for i := len(sep); i < len(s); { 

		fmt.Println("\na h ==",h )
		h *= primeRK

		//加上新的
		//h += uint32(s[i])
		h += charcode[i]
		fmt.Println("b h ==",h )

		// 去掉旧的
		//h -= pow * uint32(s[i-len(sep)])
		h -= pow * charcode[i-len(sep)]
		fmt.Println("c h ==",h )
		i++

		if h == hashsep && lastmatch <= i-len(sep) && s[i-len(sep):i] == sep {
			n++
			lastmatch = i
			fmt.Println("found n==",n ," lastmatch==",lastmatch)	

		}
	}
	return n
}

这样替换后,可以很清楚的看到运行过程是如何做的:

Count() s: 9876520210520  sep: 520
hashStr() sep: 520  hash: 520  pow: 1000

a h == 987
b h == 9876
c h == 876

a h == 876
b h == 8765
c h == 765

a h == 765
b h == 7652
c h == 652

a h == 652
b h == 6520
c h == 520
found n== 1  lastmatch== 7

a h == 520
b h == 5202
c h == 202

a h == 202
b h == 2021
c h == 21

a h == 21
b h == 210
c h == 210

a h == 210
b h == 2105
c h == 105

a h == 105
b h == 1052
c h == 52

a h == 52
b h == 520
c h == 520
found n== 2  lastmatch== 13
count== 2

另外,对于" if h == hashsep && lastmatch <= i-len(sep) && s[i-len(sep):i] == sep {
"这段,可以这样理解:

//防止计算出的hash相等,但实际串不同的情况
	if h == hashsep && s[i-len(sep):i] == sep {
		//比如Count("1111","11")这种,1111只能算2次,而不是3次
		if lastmatch <= i-len(sep) {
			n++
			lastmatch = i
		}
	}

所以才要加上lastmatch.

MAIL: [email protected]

BLOG:http://blog.csdn.net/xcl168

时间: 2024-11-08 02:04:07

Go语言源码中的Rabin-Karp算法的相关文章

详解Redis源码中的部分快速排序算法(pqsort.c)

看标题,你可能会疑惑:咦?你这家伙,怎么不讲解完整的快排,只讲一部分快排---.- 哎,冤枉."部分快排"是算法的名字,实际上本文相当详细呢.本文几乎与普通快排无异.看懂了本文,你对普通的快排也会有更深的认识了. 快速排序算法(qsort)的原理我们大都应该了解.本文介绍的是部分快速排序算法.其实其算法本质是一样的,只不过限定了排序的左右区间,也就是只对一个数字序列的一部分进行排序,故称为"部分快速排序算法",简称:pqsort Redis项目中的pqsort.c

Go语言源码中Replacer查找部份的笔记

用过strings.NewReplacer,replacer.Replace(),它按对传入参数后,能依优先级替换,并能处理中文字符串参数. 觉得功能强大,特别好用.对它的查找和优先级怎么处理有点兴趣,花时间研究了下源码,在这记录一下个人理解. package main //author:xcl //2014-1-20 记录 import ( "fmt" "strings" ) func main(){ patterns := []string{ "y&q

Redis源码中的CRC校验码(crc16、crc64)原理浅析

在阅读Redis源码的时候,看到了两个文件:crc16.c.crc64.c.下面我抛砖引玉,简析一下原理. CRC即循环冗余校验码,是信息系统中一种常见的检错码.大学课程中的"计算机网络"."计算机组成"等课程中都有提及.我们可能都了解它的数学原理,在试卷上手工计算一个CRC校验码,并不是难事.但是计算机不是人,现实世界中的数学原理需要转化为计算机算法才能实现目的.实际上作为计算机专业背景人并不会经常使用或接触到CRC的计算机算法实现的原理,通常是电子学科背景的人士

访何红辉:谈谈Android源码中的设计模式

最近Android 6.0版本的源代码开放下载,刚好分析Android源码的技术书籍<Android源码设计模式解析与实战>上市,我们邀请到它的作者何红辉,来谈谈Android源码中的设计模式,以及近期Android开发中的一些热点话题. 受访嘉宾介绍: 何红辉(@MrSimp1e),前友盟Android工程师,活跃于国内各大技术社区,热爱开源,热爱技术,热爱分享.Android开源库 AndroidEventBus . Colorful 作者, 开发技术前线 站长,<Android源码

从express源码中探析其路由机制

引言 在web开发中,一个简化的处理流程就是:客户端发起请求,然后服务端进行处理,最后返回相关数据.不管对于哪种语言哪种框架,除去细节的处理,简化后的模型都是一样的.客户端要发起请求,首先需要一个标识,通常情况下是URL,通过这个标识将请求发送给服务端的某个具体处理程序,在这个过程中,请求可能会经历一系列全局处理,比如验证.授权.URL解析等,然后定位到某个处理程序进行业务处理,最后将生成的数据返回客户端,客户端将数据结合视图模版呈现出合适的样式.这个过程涉及到的模块比较多,本文只探讨前半部分,

Android 源码中的设计模式

最近看了一些android的源码,发现设计模式无处不在啊!感觉有点乱,于是决定要把设计模式好好梳理一下,于是有了这篇文章. 面向对象的六大原则 单一职责原则 所谓职责是指类变化的原因.如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责.而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因.通俗的说,即一个类只负责一项职责,将一组相关性很高的函数.数据封装到一个类中. 开闭原则 对于扩展是开放的,这意味着模块的行为是可以扩展的.当应用的需求改变时,我们可以对模块进行扩展,使其

The Independent JPEG Group&#39;s JPEG software Android源码中 JPEG的ReadMe文件

The Independent JPEG Group's JPEG software========================================== README for release 6b of 27-Mar-1998==================================== This distribution contains the sixth public release of the Independent JPEGGroup's free JPEG

Redis源码中探秘SHA-1算法原理及其编程实现

导读 SHA-1算法是第一代"安全散列算法"的缩写,其本质就是一个Hash算法.SHA系列标准主要用于数字签名,生成消息摘要,曾被认为是MD5算法的后继者.如今SHA家族已经出现了5个算法.Redis使用的是SHA-1,它能将一个最大2^64比特的消息,转换成一串160位的消息摘要,并能保证任何两组不同的消息产生的消息摘要是不同的.虽然SHA1于早年间也传出了破解之道,但作为SHA家族的第一代算法,对我们仍然很具有学习价值和指导意义. SHA-1算法的详细内容可以参考官方的RFC:ht

从源码中浅析Android中如何利用attrs和styles定义控件

一直有个问题就是,Android中是如何通过布局文件,就能实现控件效果的不同呢?比如在布局文件中,我设置了一个TextView,给它设置了textColor,它就能够改变这个TextView的文本的颜色.这是如何做到的呢?我们分3个部分来看这个问题1.attrs.xml  2.styles.xml  3.看组件的源码. 1.attrs.xml: 我们知道Android的源码中有attrs.xml这个文件,这个文件实际上定义了所有的控件的属性,就是我们在布局文件中设置的各类属性 你可以找到attr