字符串算法KMP(字符串匹配)板子及理解

下面是求next数组的板子:

void getnext(char *b)//整个过程就相当于对自己和自己用kmp算法,但是其中一个自己要在另一个自己前面一个元素进行匹配。
{
	int n=strlen(b);
	nx[0]=-1;
	int j=0,k=-1;//其中一个自己要在另一个自己前面一个元素进行匹配。
	while(j<n)
	{
		if(k==-1||b[j]==b[k])//元素相等则j、k都++。
		{
			++j;
			++k;
			nx[j]=k;//更新next值
		}
		else k=nx[k];//元素不相等k就回溯,由于j在k前面,所以此时nx[k]是已经被算出来了的。
	}
}

下面是kmp算法的板子(a是主串,b是模式串):

inline int kmp(char *a,char *b)
{
	int ans=0;
	getnext(b);
	int m=strlen(a);
	int n=strlen(b);
	int i=0,j=0;
	while(i<m)
	{
		if(j==-1||b[j]==a[i])//j==-1的情况是第一个字母就不匹配。如果字母匹配i和j就同时后移 
		{
			++i;
			++j;
		}
		else j=nx[j];//如果匹配失败,j回溯。
		if(j==n)//匹配成功就ans++,j回溯。
		{
			ans++;
			j=nx[j];
		}
	}
	return ans;
}

KMP算法实际上就是通过对模式串进行预处理来缩短匹配时间时间复杂度是O(m+n)next数组记录着如果匹配失败应当将在模式串上的指针移动到模式串的哪个位置(具体的移动方式是:当主串S与模式串P失配时,j=next[j](这一步是将j回溯,使得j刚好停在最长公共前后缀的后面一位,因为前面是匹配的(公共前后缀),所以从j开始比较即可),P向右移动j - next[j](用next [j] 处的字符继续跟主串i 处的字符匹配,相当于模式串向右移动 j - next[j] 位))。从本质上讲next数组的含义是除了当前字符外,当前字符之前的字符串的最长公共前后缀的长度,next数组是用公共前后缀来实现的。
不定义为内联函数似乎调用函数的时间开销很大,所以在多次调用函数时要把函数定义为内联函数,这样就减少了因调用函数而产生的时间开销
b站有个视频讲的很清楚:
链接

注:如果想要求两个字符串的最长公共前后缀(比如sample和please的最长公共前后缀是ple,其长度为3),只需要将两个字符串按一定顺序拼接后求拼接后字符串的next数组,求出的next数组的最后一个数字就是答案。
想要快一点可以利用下面的kmp函数的返回值,其返回值就是答案。

inline int kmp(char *s,char *t)
{
	int len=strlen(s);
    int n=strlen(t);
    int i=max(0,len-n),j=0;
    getnext(t);
    for(i;i<len;)
    {
        while(s[i]!=t[j])
        {
            if(nx[j]==0)
            {
                j=0;
                break;
            }
            else j=nx[j];
        }
        if(s[i]==t[j])
        {
            j++;
        }
        i++;
    }
    return j;
}

一些用得频繁的与字符串操作有关的函数:

1.memcpy函数的速度要比strcpy快,它比strcpy多了一个第三个参数用来规定要复制的字节数,而且要注意memcpy不会在最后加上结束符\0,strcpy会在最后加\0。

2.strcat函数的速度较慢,因为每次都要遍历到\0后再进行拼接,想提高速度可以手写。

提醒一句:没有返回值的函数类型一定要写void,不然交到oj上会RE,而且平时也可能出现一些问题,luoguP3375是血的教训= =、

2021/4/16
今天上数据结构课学了next数组的优化方式,有效优化了某些特殊情况下kmp的时间复杂度,就是在原next数组的基础上,如果一个元素等于它回溯值处的元素,那么这个元素的回溯值就是它回溯值处元素的回溯值。(证明可以自己手动画一下,其实也不是很难想,因为当前元素不匹配的话,如果回溯值处的元素与它相等肯定也不匹配啊。)

下面是代码:

void getnext(char *b)
{
	int n=strlen(b);
	nx[0]=-1;
	int j=0,k=-1;
	while(j<n)
	{
		if(k==-1||b[j]==b[k])
		{
			++j;
			++k;
			if(b[j]!=b[k])//如果一个元素不等于它回溯值处的元素
			nx[j]=k;//那么它的回溯值该是啥还是啥 
			else nx[j]=nx[k];//如果一个元素等于它回溯值处的元素,那么这个元素的回溯值就是它回溯值处元素的回溯值 
		}
		else k=nx[k];
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种用于字符串匹配算法,它的核心思想是利用部分匹配表来避免不必要的比较。下面是KMP算法的原理和实现步骤: 1. 部分匹配表的计算: - 部分匹配值是指字符串的前缀和后缀的最长公共部分的长度。 - 部分匹配表是一个数组,记录了每个位置的部分匹配值。 - 部分匹配表的计算可以通过动态规划的方式进行,具体步骤如下: - 初始化部分匹配表的第一个元素为0。 - 从第二个元素开始,依次计算每个位置的部分匹配值: - 如果当前位置的字符与前一个位置的部分匹配值对应的字符相等,则部分匹配值加1。 - 如果不相等,则需要回溯到前一个位置的部分匹配值对应的字符的部分匹配值,继续比较。 - 在主串中从左到右依次比较字符,同时在模式串中根据部分匹配表进行跳跃。 - 如果当前字符匹配成功,则继续比较下一个字符。 - 如果当前字符匹配失败,则根据部分匹配表找到模式串中需要跳跃的位置,继续比较。 下面是一个使用KMP算法进行字符串匹配的示例代码: ```python def kmp_search(text, pattern): n = len(text) m = len(pattern) next = get_next(pattern) i = 0 j = 0 while i < n and j < m: if j == -1 or text[i] == pattern[j]: i += 1 j += 1 else: j = next[j] if j == m: return i - j else: return -1 def get_next(pattern): m = len(pattern) next = [-1] * m i = 0 j = -1 while i < m - 1: if j == -1 or pattern[i] == pattern[j]: i += 1 j += 1 next[i] = j else: j = next[j] return next ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值