文章目录
kmp算法
kmp算法是比对str1(长度为n)中是否包含str2(长度为s)的算法。
小tips:暴力解的方法,就是O(sn),但看下面这个例子:
a与d不同,说明字符串不匹配,此时若为暴力解,将变成下面这个样子。
又从头开始匹配,但看kmp算法接下来会怎么做?
这是为什么?
kmp算法本质是了解自身寻找字符串的特点而避免重复匹配加速暴力算法的过程
在刚开始图中
acac与acac都是匹配成功的,且str2字符串d前满足一个特点
A
.
.
.
A
A...A
A...A
即首尾是相同的(A=ac )(…=无)
因此可以更好理解上图,part2与part3已经比对成功相等,而part2与part1也比对相同(我们了解str2的特点),自然part1与part3不用比对,可以直接跳到下面这幅图。
定义str2的特点–next数组
next数组指的是 当str1某一位与str2某一位不匹配时,j指针应该跳到哪一位呢?
给大家几个场景,分别该跳到哪一位呢?
这里str2中d前字符串,不满足A…A的特点,因此跳到下图
(j指针跳到0位置,i指针不动),那我们next数组中next[4]=0
这里又跳到哪里呢?
我们发现str2中h前满足A…A的特点,跳到下图
即i指针不同,j指针由6位置跳到2位置,即针对此字符串str2,next[6]=4
这里跳到哪里呢???
跳到下图
(i指针+1,j指针不动,这里我们next数组定义成-1,即next[0]=-1)
审视下面的next数组
- 第0位不等的话,i指针+1,j指针不动,设成-1
- 第1位不等的话,审视c前的字符串,a,不满足A…A的特点,j指针跳到0
- 第2位不等的话,审视a前的字符串,ac,不满足A…A的特点,j指针跳到0
- 第3位不等的话,审视c前的字符串,aca,满足A…A的特点,j指针跳到A的后面一位,即1
- 第4位不等的话,审视a前的字符串,acac,满足A…A的特点,j指针跳到A的后面一位,即2
- 第5位不等的话,审视f前的字符串,acaca,满足A…A的特点,j指针跳到A(aca,A与A重叠了)的后面一位,即3
- 第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
-
next数组的改进,很好理解
-
next数组代表j指针要跳到的位置,那假如next[a]=b,即与s2[a]字符不等,j指针要从a跳到b,那假如s2[b]==s2[a],还会不等,还要往前跳,因此我们可以把next数组改成跳的最终位置,即nextval
-
举例
-
代码
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的子树,必须是完整的,子树包含完整的根节点:
暴力解:
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算法对面“后面的字符串”是不是前面的子串