KMP算法的核心是计算模板字符串的最大前缀和后缀的公共部分的最大长度
,但不包括字符串本身,否则最大长度始终是字符串本身。
一:步骤:
1.获得模板字符串每个位置的最大公共长度,我们用数组next存储。
2.利用模板字符串每个位置的最大公共长度,对模板字符串和目标字符串进
行对比,当每次比较到两个字符串的字符不同时,我们根据最大公共长度将
模板字符串进行概念上的前移。
二:那么什么是概念上的前移呢?
例如:
下标: 0 1 2 3 4 5 6 7 8 9 10 11 12
模板字符串:b a b d c
目标字符串:a b a b c a b a b a b d c
为了方便说明,我将next数组提前求出,求next的方法等会说
下标: 0 1 2 3 4
next:-1 0 0 1 0
对模板串和目标串进行比较,模板串的bab下一个是d,目标串的bab下一个
是c,出现不同,利用next数组,模板串的下标将从3到1,(next[3] = 1)
,相当于将模板串向前移动了两位。如下:
移动前:
下标: 0 1 2 3 4 5 6 7 8 9 10 11 12
模板字符串: b a b d c
目标字符串: a b a b c a b a b a b d c
移动后:
下标: 0 1 2 3 4 5 6 7 8 9 10 11 12
模板字符串: b a b d c
目标字符串:a b a b c a b a b a b d c
三:求最大公共部分
1.next数组的实现
网上找的代码:
int[] GetNextVal(string smallstr)
{
//前缀串起始位置("-1"是方便计算)
int k = -1;
//后缀串起始位置("0"是方便计算)
int j = 0;
int[] next = new int[smallstr.Length];
//根据公式: j=0时,next[j]=-1
next[j] = -1;
while (j < smallstr.Length - 1)
{
if (k == -1 || smallstr[k] == smallstr[j])
{
//pk=pj的情况:
next[j+1]=k+1 => next[j+1]=next[j]+1
next[++j] = ++k;
}
else
{
//pk != pj 的情况:我们递推 k=next[k];
//要么找到,要么k=-1中止
k = next[k];
}
}
return next;
}
k为最大公共长度,j为已匹配的字符数。
当两个字符不相同,我们可以将长度为next[j]的字符串进行分割,这和前
面讲的概念上的前移思想相同。获得其最大公共长度next[k],然后再和位
置i的字符比较。这是因为长度为next[j]前缀和后缀都可以分割成上部的构
造,如果位置next[k]和位置i的字符相同,则next[i+1]就等于next[k]加1
。如果不相等,就可以继续分割长度为next[k]的字符串,直到字符串长度
为0为止.
四:完整代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SupportCenter.Test
{
public class Program
{
static void Main(string[] args)
{
string zstr = "ababcabababdc";
string mstr = "babdc";
var index = KMP(zstr, mstr);
if (index == -1)
Console.WriteLine("没有匹配的字符串!");
else
Console.WriteLine("哈哈,找到字符啦,位置为:" + index);
Console.Read();
}
static int KMP(string bigstr, string smallstr)
{
int i = 0;
int j = 0;
//计算“前缀串”和“后缀串“的next
int[] next = GetNextVal(smallstr);
while (i < bigstr.Length && j < smallstr.Length)
{
if (j == -1 || bigstr[i] == smallstr[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
if (j == smallstr.Length)
return i - smallstr.Length;
return -1;
}
/// <summary>
/// p0,p1....pk-1 (前缀串)
/// pj-k,pj-k+1....pj-1 (后缀串)
/// </summary>
/// <param name="match"></param>
/// <returns></returns>
static int[] GetNextVal(string smallstr)
{
//前缀串起始位置("-1"是方便计算)
int k = -1;
//后缀串起始位置("-1"是方便计算)
int j = 0;
int[] next = new int[smallstr.Length];
//根据公式: j=0时,next[j]=-1
next[j] = -1;
while (j < smallstr.Length - 1)
{
if (k == -1 || smallstr[k] == smallstr[j])
{
//pk=pj的情况: next[j+1]=k+1 => next[j+1]=next[j]+1
next[++j] = ++k;
}
else
{
//pk != pj 的情况:我们递推 k=next[k];
//要么找到,要么k=-1中止
k = next[k];
}
}
return next;
}
}
}
参考博客
http://www.cnblogs.com/huangxincheng/archive/2012/12/01/2796993.html
http://billhoo.blog.51cto.com/2337751/411486/
http://blog.csdn.net/yutianzuijin/article/details/11954939/
,但不包括字符串本身,否则最大长度始终是字符串本身。
一:步骤:
1.获得模板字符串每个位置的最大公共长度,我们用数组next存储。
2.利用模板字符串每个位置的最大公共长度,对模板字符串和目标字符串进
行对比,当每次比较到两个字符串的字符不同时,我们根据最大公共长度将
模板字符串进行概念上的前移。
二:那么什么是概念上的前移呢?
例如:
下标: 0 1 2 3 4 5 6 7 8 9 10 11 12
模板字符串:b a b d c
目标字符串:a b a b c a b a b a b d c
为了方便说明,我将next数组提前求出,求next的方法等会说
下标: 0 1 2 3 4
next:-1 0 0 1 0
对模板串和目标串进行比较,模板串的bab下一个是d,目标串的bab下一个
是c,出现不同,利用next数组,模板串的下标将从3到1,(next[3] = 1)
,相当于将模板串向前移动了两位。如下:
移动前:
下标: 0 1 2 3 4 5 6 7 8 9 10 11 12
模板字符串: b a b d c
目标字符串: a b a b c a b a b a b d c
移动后:
下标: 0 1 2 3 4 5 6 7 8 9 10 11 12
模板字符串: b a b d c
目标字符串:a b a b c a b a b a b d c
三:求最大公共部分
1.next数组的实现
网上找的代码:
int[] GetNextVal(string smallstr)
{
//前缀串起始位置("-1"是方便计算)
int k = -1;
//后缀串起始位置("0"是方便计算)
int j = 0;
int[] next = new int[smallstr.Length];
//根据公式: j=0时,next[j]=-1
next[j] = -1;
while (j < smallstr.Length - 1)
{
if (k == -1 || smallstr[k] == smallstr[j])
{
//pk=pj的情况:
next[j+1]=k+1 => next[j+1]=next[j]+1
next[++j] = ++k;
}
else
{
//pk != pj 的情况:我们递推 k=next[k];
//要么找到,要么k=-1中止
k = next[k];
}
}
return next;
}
k为最大公共长度,j为已匹配的字符数。
当两个字符不相同,我们可以将长度为next[j]的字符串进行分割,这和前
面讲的概念上的前移思想相同。获得其最大公共长度next[k],然后再和位
置i的字符比较。这是因为长度为next[j]前缀和后缀都可以分割成上部的构
造,如果位置next[k]和位置i的字符相同,则next[i+1]就等于next[k]加1
。如果不相等,就可以继续分割长度为next[k]的字符串,直到字符串长度
为0为止.
四:完整代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SupportCenter.Test
{
public class Program
{
static void Main(string[] args)
{
string zstr = "ababcabababdc";
string mstr = "babdc";
var index = KMP(zstr, mstr);
if (index == -1)
Console.WriteLine("没有匹配的字符串!");
else
Console.WriteLine("哈哈,找到字符啦,位置为:" + index);
Console.Read();
}
static int KMP(string bigstr, string smallstr)
{
int i = 0;
int j = 0;
//计算“前缀串”和“后缀串“的next
int[] next = GetNextVal(smallstr);
while (i < bigstr.Length && j < smallstr.Length)
{
if (j == -1 || bigstr[i] == smallstr[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
if (j == smallstr.Length)
return i - smallstr.Length;
return -1;
}
/// <summary>
/// p0,p1....pk-1 (前缀串)
/// pj-k,pj-k+1....pj-1 (后缀串)
/// </summary>
/// <param name="match"></param>
/// <returns></returns>
static int[] GetNextVal(string smallstr)
{
//前缀串起始位置("-1"是方便计算)
int k = -1;
//后缀串起始位置("-1"是方便计算)
int j = 0;
int[] next = new int[smallstr.Length];
//根据公式: j=0时,next[j]=-1
next[j] = -1;
while (j < smallstr.Length - 1)
{
if (k == -1 || smallstr[k] == smallstr[j])
{
//pk=pj的情况: next[j+1]=k+1 => next[j+1]=next[j]+1
next[++j] = ++k;
}
else
{
//pk != pj 的情况:我们递推 k=next[k];
//要么找到,要么k=-1中止
k = next[k];
}
}
return next;
}
}
}
参考博客
http://www.cnblogs.com/huangxincheng/archive/2012/12/01/2796993.html
http://billhoo.blog.51cto.com/2337751/411486/
http://blog.csdn.net/yutianzuijin/article/details/11954939/