直击面试之KMP字符串查找

8 篇文章 0 订阅
4 篇文章 0 订阅

面试时碰到一个算法题,判断一个字符串是不是一段字符的子串,当时不加思索的就写出两段for循环,伪代码如下

#src为源串   pat为子串
i=0
while(i<len(src) && len(src) >== i + len(pat)){
    j=0
    while(j<len(pat) && i+j<len(src) && src[i+j]==pat[j]){
        j++
    }

    if(j==len(pat){
        break;
    }

    i++
}

若源串长度为n,匹配串长度为m,则最坏情况下时间复杂度为O(m*(n-m+1))

 

下面就我理解的KMP算法进行记录

KMP算法关键是对匹配字符串进行一些处理,首先分配一个跟匹配字符同样长度的最大前缀匹配的长度。

比如下面的字符串对应的最大前缀匹配长度

字符串ABCDE各字符对应的最大匹配前缀长度
ABCDE
00000
字符串AAAAB
AAAAB
01230
字符串AABAA
AABAA
01012
字符串ABAAA
ABAAA
00111

下面使用GO语言实现获取最大匹配前缀长度

func getLpsArray(pat string) []int {
	ret := make([]int, len(pat))
	lps := 0 //最大前缀匹配长度
	//首字母匹配长度为0
	for i := 1; i < len(pat); i++ {
		if pat[i] == pat[lps] {
			lps++
			ret[i] = lps
		} else {
			if lps != 0 {
				lps = ret[lps-1]
				i--
			}
		}
	}

	//fmt.Println(ret)
	return ret
}

下面以AABAA阐述这段代码

首字母lps全部为0 即 lps=0 ret[0]=0

循环i=1时 lps=0 pat[1]=‘A’与pat[0]='A'相等,最大匹配长度加1 lps=1 当前字符的最大匹配前缀为1 ret[1]=1

循环i=2时 lps=1 pat[2]='B'与pat[1]='A'不符,需继续匹配最大前缀前一个字符,此时lps不为0,更新lps=ret[lps-1],lps=ret[0]=0, 继续与pat[2]进行匹配

循环i=2     lps=0 pat[2]='B'与pat[0]='A'不符,此时lps=0,即ret[2]=0

循环i=3时 lps=0 pat[3]='A'与pat[0]='A'相等,最大匹配长度加1 lps=1 当前字符的最大匹配前缀为1 ret[3]=1

循环i=4时 lps=1 pat[3]='A'与pat[0]='A'相等,最大匹配长度加1 lps=2 当前字符的最大匹配前缀为1 ret[4]=2

下面实现KMP查找

//src为源串  pat匹配串  返回值为匹配串在源串中的索引位置
func KmpSearch(src, pat string) int {
	if len(pat) < 1 || len(src) < len(pat) {
		return -1
	}

	iRet := -1
	for i, j, lps := 0, 0, getLpsArray(pat); i < len(src); i++ {
		if src[i] == pat[j] {
			j++
			if j == len(pat) {
				//找到相匹配的
				iRet = i - len(pat) + 1
				break
			}
		} else {
			if j > 0 {
				j = lps[j-1]
				i--
			}
		}
	}

	return iRet
}

 以src="AAAABAA" pat="AABAA"阐述代码

构建lps=[0,1,0,1,2]

i=0时 j=0,src[0]=pat[0]='A', i,j自增 i=1,j=1

i=1j=1,src[1]=pat[1]='A', i,j自增 i=2,j=2

i=2 j=2,src[2]='A' pat[2]='B"不相等,此时j=2,更新j为上一次匹配j=1时lps[1]对应的值1,即j=1

i=2  j=1,src[2]=pat[1]='A', i,j自增 i=3,j=2

i=3 j=2,src[3]='A' pat[2]='B"不相等,此时j=2,更新j为上一次匹配j=1时lps[1]对应的值1,即j=1

i=3  j=1,src[3]=pat[1]='A', i,j自增 i=4,j=2

i=4  j=1,src[4]=pat[2]='B', i,j自增 i=5,j=3

i=5  j=3,src[5]=pat[3]='A', i,j自增 i=6,j=4

i=6  j=4,src[6]=pat[4]='A' 此时找到匹配串退出循环

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值