(1) 参考网址:
KMP算法: http://www.cnblogs.com/dolphin0520/archive/2011/08/24/2151846.html
--- 用递推思想和直接法两种方式求解next数组
KMP学习心得: http://www.java3z.com/cwbwebhome/article/article19/res023.html?id=3737
--- 当一个字符串以0为起始下标时,next[i]可以描述为"不为自身的最大首尾重复子串长度"。
KMP字符串模式匹配中模式函数: http://www.java3z.com/cwbwebhome/article/article19/re022.html?id=3731
--- 详细讲解了next数组的求解方法【next数组求解的改进版本】
(2) 经典总结:
一、怎么求串的模式值next[n]
定义:
(1)next[0]= –1 意义:任何串的第一个字符的模式值规定为-1。
(2)next[j]= –1 意义:模式串T中下标为j的字符,如果与首字符相同,且j的前面的1~k个字符与开头的1~k个字符不等或者相等但
T[k]==T[j](1≤k<j)。 如:T=”abCabCad” 则 next[6]=-1,因T[3]=T[6] bCa
(3)next[j]=k 意义:模式串T中下标为j的字符,如果j的前面k个字符与开头的k个字符相等,且T[j] != T[k] (1≤k<j)。
即T[0]T[1]T[2]...T[k-1]== T[j-k]T[j-k+1]T[j-k+2]…T[j-1] 且T[j] != T[k].(1≤k<j);
(4)next[j]=0 意义:除(1)(2)(3)的其他情况。
二、next 函数值究竟是什么含义
设在字符串S中查找模式串T,若S[m]!=T[n],那么,取T[n]的模式函数值next[n],
1. next[n]= -1 表示S[m]和T[0]间接比较过了,不相等,下一次比较 S[m+1] 和T[0]
2. next[n]=0 表示比较过程中产生了不相等,下一次比较 S[m] 和T[0]。
3. next[n]= k >0 但k<n, 表示,S[m]的前k个字符与T中的开始k个字符已经间接比较相等了,下一次比较S[m]和T[k]相等吗?
4. 其他值,不可能。
(3) 代码实现 【参考书籍:《数据结构与算法 Java语言描述》 邓俊辉 著】
原书代码:【略有改动】
package
dsa;
/*
* 串模式匹配:KMP算法
*/
public
class
PM_KMP {
public
static
void
main(String[] args) {
System.out.println(PM(
"ababacab"
,
"aca"
));
}
public
static
int
PM(String T, String P) {
// KMP算法
int
[] next = BuildNextImproved(P);
// 构造next[]表
int
i =
0
;
// 主串指针
int
j =
0
;
// 模式串指针
while
(j < P.length() && i < T.length()) {
// 自左向右逐个比较字符
ShowProgress(T, P, i - j, j);
ShowNextTable(next, i - j, P.length());
System.out.println();
if
(
0
> j || T.charAt(i) == P.charAt(j)) {
// 若匹配,或P已移出最左侧(提问:这两个条件能否交换次序?)
i++;
j++;
// 则转到下一对字符
}
else
// 否则
j = next[j];
// 模式串右移(注意:主串不用回退)
}
// while
return
(i - j);
}
protected
static
int
[] BuildNext(String P) {
// 建立模式串P的next[]表
int
[] next =
new
int
[P.length()];
// next[]表
int
j =
0
;
// “主”串指针
int
t = next[
0
] = -
1
;
// “模式”串指针
while
(j < P.length() -
1
)
if
(
0
> t || P.charAt(j) == P.charAt(t)) {
// 匹配
j++;
t++;
next[j] = t;
// 此句可以改进...
}
else
// 失配
t = next[t];
for
(j =
0
; j < P.length(); j++)
System.out.print(
"\t"
+ P.charAt(j));
System.out.print(
"\n"
);
ShowNextTable(next,
0
, P.length());
return
(next);
}
protected
static
int
[] BuildNextImproved(String P) {
// 建立模式串P的next[]表(改进版本)
int
[] next =
new
int
[P.length()];
;
// next[]表
int
j =
0
;
// “主”串指针
int
t = next[
0
] = -
1
;
// “模式”串指针
while
(j < P.length() -
1
)
if
(
0
> t || P.charAt(j) == P.charAt(t)) {
// 匹配
j++;
t++;
next[j] = (P.charAt(j) != P.charAt(t)) ? t : next[t];
// 注意此句与未改进之前的区别
}
else
// 失配
t = next[t];
for
(j =
0
; j < P.length(); j++)
System.out.print(
"\t"
+ P.charAt(j));
System.out.print(
"\n"
);
ShowNextTable(next,
0
, P.length());
return
(next);
}
protected
static
void
ShowNextTable(
int
[] N,
int
offset,
int
length) {
// 显示next[]表,供演示分析
int
i;
for
(i =
0
; i < offset; i++)
System.out.print(
"\t"
);
for
(i =
0
; i < length; i++)
System.out.print(
"\t"
+ N[i]);
System.out.print(
"\n\n"
);
}
protected
static
void
ShowProgress(
// 动态显示匹配进展
String T,
// 主串
String P,
// 模式串
int
i,
// 模式串相对于主串的起始位置
int
j)
// 模式串的当前字符
{
int
t;
System.out.println(
"-------------------------------------------"
);
for
(t =
0
; t < T.length(); t++)
System.out.print(
"\t"
+ T.charAt(t));
System.out.print(
"\n"
);
if
(
0
<= i + j) {
for
(t =
0
; t < i + j; t++)
System.out.print(
"\t"
);
System.out.print(
"\t|"
);
}
System.out.println();
for
(t =
0
; t < i; t++)
System.out.print(
"\t"
);
for
(t =
0
; t < P.length(); t++)
System.out.print(
"\t"
+ P.charAt(t));
System.out.print(
"\n"
);
System.out.println();
}
}
|
精简代码:
package
dsa.me;
public
class
Kmp {
public
static
void
main(String[] args) {
System.out.println(kmp(
"ababa"
,
"bab"
));
System.out.println(kmp(
"ababa"
,
"bba"
));
}
// KMP算法
public
static
int
kmp(String T, String P) {
int
next[] = buildeNextImproved(P);
int
i =
0
;
int
j =
0
;
while
(i < T.length() && j < P.length()) {
// 一定要限制它们在范围内,不然会报错
if
(j <
0
|| T.charAt(i) == P.charAt(j)) {
// 匹配,i和j都向右移动,j<0(j=-1)
i++;
j++;
}
else
{
// 不匹配,只要移动j,i不要回溯
j = next[j];
}
}
if
(j >= P.length())
return
(i - j);
else
return
-
1
;
}
// 求next数组
public
static
int
[] buildeNext(String P) {
int
[] next =
new
int
[P.length()];
int
j =
0
;
int
t = -
1
;
// 初始值是-1
next[
0
] = -
1
;
while
(j < P.length() -
1
) {
if
(t == -
1
|| P.charAt(t) == P.charAt(j)) {
// 匹配---如果在j这里不存在首尾相同的字符串,那么next[j]就会等于0
t++;
j++;
next[j] = t;
// 由这里看出while条件中j不能等于P.length()-1
}
else
{
// 不匹配
t = next[t];
}
}
return
next;
}
// 求next数组的改进版本
public
static
int
[] buildeNextImproved(String P) {
int
[] next =
new
int
[P.length()];
int
j =
0
;
int
t = -
1
;
// 初始值是-1
next[
0
] = -
1
;
while
(j < P.length() -
1
) {
if
(t == -
1
|| P.charAt(t) == P.charAt(j)) {
// 匹配---如果在j这里不存在首尾相同的字符串,那么next[j]就会等于0
t++;
j++;
next[j] = (P.charAt(j) != P.charAt(t)) ? t : next[t];
// 改进的地方
}
else
{
// 不匹配
t = next[t];
}
}
return
next;
}
}
|
附上参考书籍的对应章节pdf下载地址:http://115.com/file/dpkg6art#数据结构与算法(Java_描述).pdf