kmp算法理解及手写代码

kmp算法

kmp算法是比对str1(长度为n)中是否包含str2(长度为s)的算法。

小tips:暴力解的方法,就是O(sn),但看下面这个例子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kwaUp18H-1609902020557)(A448D49100A84A0A849C310F28048565)]

a与d不同,说明字符串不匹配,此时若为暴力解,将变成下面这个样子。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AhAgsDv5-1609902020563)(A01AEBDBA76B4332B6F973976649638B)]

又从头开始匹配,但看kmp算法接下来会怎么做?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rbya6R0y-1609902020569)(388ABA02FE214A8998B276175CFA3E22)]

这是为什么?

kmp算法本质是了解自身寻找字符串的特点而避免重复匹配加速暴力算法的过程

在刚开始图中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B35m9ydF-1609902020573)(6DFDCF48CED44FE1B6CC6FCE9AA52F85)]

acac与acac都是匹配成功的,且str2字符串d前满足一个特点

A . . . A A...A A...A
即首尾是相同的(A=ac )(…=无)

因此可以更好理解上图,part2与part3已经比对成功相等,而part2与part1也比对相同(我们了解str2的特点),自然part1与part3不用比对,可以直接跳到下面这幅图。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3q9zdtL7-1609902020582)(5000F41F7B784C17A2A983E8D9D003B4)]

定义str2的特点–next数组

next数组指的是 当str1某一位与str2某一位不匹配时,j指针应该跳到哪一位呢?
给大家几个场景,分别该跳到哪一位呢?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gLKmYNgc-1609902020587)(7D38D9F8A6C849ECB0A6E51AE663C022)]

这里str2中d前字符串,不满足A…A的特点,因此跳到下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1C8ppwkt-1609902020590)(3A337B369C0944778A23A5BFAB657A5D)]

(j指针跳到0位置,i指针不动),那我们next数组中next[4]=0

这里又跳到哪里呢?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iplvHmus-1609902020593)(B742FC799BB141C59BDC2175BEBEDE2D)]

我们发现str2中h前满足A…A的特点,跳到下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XVLeFYzt-1609902020607)(DCE3C5DCB989437C9C71D2F8E9E65B58)]

即i指针不同,j指针由6位置跳到2位置,即针对此字符串str2,next[6]=4

这里跳到哪里呢???
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sBzuVxCh-1609902020609)(BFF39C06E17544219D97C5814AEF26E9)]

跳到下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vTX79UQV-1609902020611)(2F204F26BD08423FA4F62F25ED9C2088)]

(i指针+1,j指针不动,这里我们next数组定义成-1,即next[0]=-1)

审视下面的next数组
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a4m2kdwk-1609902020624)(B050060DF0DF4B949A614FF279C67CE2)]

  1. 第0位不等的话,i指针+1,j指针不动,设成-1
  2. 第1位不等的话,审视c前的字符串,a,不满足A…A的特点,j指针跳到0
  3. 第2位不等的话,审视a前的字符串,ac,不满足A…A的特点,j指针跳到0
  4. 第3位不等的话,审视c前的字符串,aca,满足A…A的特点,j指针跳到A的后面一位,即1
  5. 第4位不等的话,审视a前的字符串,acac,满足A…A的特点,j指针跳到A的后面一位,即2
  6. 第5位不等的话,审视f前的字符串,acaca,满足A…A的特点,j指针跳到A(aca,A与A重叠了)的后面一位,即3
  7. 第6位不等的话,审视g前的字符串,acacaf,不满足A…A的特点,j指针跳到0
代码
public class main {
	//得到next数组
	//编写该函数代码是理解如何由next[i-1]得到next[i]
	//next[i-1]=t  代表前t位(0~t-1)  等于后面t位(到i-2位)
	//因此若s[t]==s[i-1]  则next[i]=t+1
	//否则最后一位不等代表不满足A...A的特点,直接为0
	public static int[] getnext(String s)
	{
		int[] next=new int[s.length()];
		next[0]=-1;
		for(int i=1;i<next.length;i++)
		{
			if(i==1)
			{
				next[1]=0;
			}
			else {
				if(s.charAt(next[i-1])==s.charAt(i-1))
				{
					next[i]=next[i-1]+1;
				}
				else {
					next[i]=0;
				}
			}
		}
		return next;
	}
	public static boolean kmpmatch(String s1,String s2)
	{
		int[] next=getnext(s2);
		int i=0,j=0;
		for(;i<s1.length();i++)
		{
			if(j==s2.length())
			{
				return true;
			}
			if(s1.charAt(i)==s2.charAt(j))
			{
			
				j+=1;
			}
			else
			{
				if(j==0)
				{
					i=i+1;
				}
				else {
					j=next[j];
				}
			}
		}
		return false;
		
	}
	
	
	public static void main(String[] args)
	{
		
		System.out.println(kmpmatch("adaddefg", "ac"));
	}
   
	

nextval
  1. next数组的改进,很好理解

  2. next数组代表j指针要跳到的位置,那假如next[a]=b,即与s2[a]字符不等,j指针要从a跳到b,那假如s2[b]==s2[a],还会不等,还要往前跳,因此我们可以把next数组改成跳的最终位置,即nextval

  3. 举例
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BU89Zx63-1609902611201)(DB06AD98B1E741D6BD7476C8A95092D1)]

  4. 代码

	public static int[] getnextval(String s)
	{
		int[] next=getnext(s);
		for(int i=0;i<next.length;i++)
		{
			if(s.charAt(i)==s.charAt(next[i]))
			{
				next[i]=next[next[i]];
			}
		}
		return next;
	}
kmp的应用

经典题目1:str1:“123456”,有很多的旋转词,“123456”,“23456
“345612”…
判断str1是否是str2的旋转词?

将str1扩展成str1str1,然后看str2是不是其子串即可,这里用kmp即可。

经典题目2:问tree2是否是tree
1的子树,必须是完整的,子树包含完整的根节点:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dnRX3c4u-1609998685690)(43FA13799BE94B73AC917F4F59427878)]

暴力解:

public static class TreeNode {
	     int val;
	     TreeNode left;
	     TreeNode right;
	     TreeNode(int x) { val = x; }
	 }
	public static boolean ischildtree(TreeNode root1,TreeNode root2)
	{
		
		if(root1==null||root2==null)
		{
			return false;
		}
		if(issametree(root1, root2))
		{
			return true;
		}
		return ischildtree(root1.left, root2)||ischildtree(root1.right, root2);
		
	}
	
	
	public static boolean issametree(TreeNode root1,TreeNode root2)
	{
		if((root1==null&&root2!=null)||(root1!=null&&root2==null))
		{
			return false;
		}
		if(root1==null&&root2==null)
		{
			return true;
		}
		if(root1.val==root2.val)
		{
			return issametree(root1.left, root2.left)&&issametree(root1.right, root2.right);
		}
		else {
			return false;
		}
		
	}

其本质也是字符串对比,将每棵树都补成满二叉树用数组顺序存储,然后利用kmp算法对面“后面的字符串”是不是前面的子串

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ij3y9OY3-1609998685694)(F4D9753D86F54F4BAB9B44CD20A77E59)]

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值