在文本中查找对应的模式成为字符串匹配:
文本:T
模式:P
一:朴素的字符串匹配算法
思路:(1)首先有两个游标i,j指向文本和模式的位置
(2)当T[i]=P[j]时i++,j++,继续往下比较。
(3)当T[i]!=P[j]时,i回溯到上次开始比较的下一位,j回溯到0
(4)当找到一段连续的字符与模式字符串匹配最终模式字符串匹配成功,返回此次开始比较的文本位置。
int native_stringmatch(string *T,string *P)
{
int n=strlen(T);
int m=strlrn(P);
int s=0,i=0;
for(s=0;s<n-m;s++)//当文本串中剩余没有比较的元素的长度小于模式串时就不用在比了,是找不到的
{
for(i=0;i<m;i++)
{
if(T[s+i]!=P[i])//当出现不匹配时此次比较结束
break;
if(i==m-1)//当P[i]==T[s+i]一直到了模式串的最后一个元素,则表明匹配成功
{
cout<<"匹配成功";
return s;
}
}
}
return -1;
}
最坏时间复杂度为O((n-m+1)*m),性能较差
KMP算法
个人愚见:
文本串:s
模式串:P
s的长度为n,P的长度为m,且m<<n
引入概念失败数组:next[j]=k;
(1)最开始当然是s的首字符与p的首字符比较,如果不相等就下一个与p的首字符比较,设s的下标为i,P的下标为j。刚开始时i=j=0;
(2)当s[i]≠[j]时,匹配失败不要回溯i。以为在此之前可能有已经匹配好的元素,如果在此进行匹配会浪费时间。此时移动j,使得j=next[j];用s[i]与p[j]比较。
(3)情况:当j=-1时则用s[i+1]和p0比较
(4)当最终j==p的长度的时候,匹配成功,此时,在模式串中的位置为:i-j;
解释一下next[j],对于p中的每一个元素都求出其对应的next[j];对于p[j]其next值应为:p0p1p2p3p4pj-1中真前缀和真后缀中相同的最长长度。对于j=0时,其为-1,当没有相同的真前缀和真后缀时,next[j]=0;
1 /**
2 * KMP算法
3 *
4 * @param ss 主串
5 * @param ps 模式串
6 * @return 如果找到,返回在主串中第一个字符出现的下标,否则为-1
7 */
8 public static int KMP(String ss, String ps) {
9 char[] s = ss.toCharArray();
10 char[] p = ps.toCharArray();
11
12 int i = 0; // 主串的位置
13 int j = 0; // 模式串的位置
14 int[] next = getNext(ps);
15 while (i < s.length && j < p.length) {
16 //①如果j=-1,或者当前字符匹配成功(即S[i]==P[j]),都令i++,j++
17 if (j == -1 || s[i] == p[j]) { // 当j为-1时,要移动的是i,当然j也要归0
18 i++;
19 j++;
20 } else {
21 //②如果j!=-1,且当前字符匹配失败(即S[i]!=P[j]),则令i不变,j=next[j],j右移j-next[j]
22 j = next[j];
23 }
24 }
25 if (j == p.length) {
26 return i - j;
27 } else {
28 return -1;
29 }
30 }
next[j]的求法:递归)
最大长度表右移一位,然后初值为-1;
public int[] getNext(String ps) {
2 char[] p = ps.toCharArray();
3 int[] next = new int[p.length];
4 next[0] = -1;
5 int j = 0;
6 int k = -1;
7 while (j < p.length - 1) {
8 //p[k]表示前缀,p[j]表示后缀
9 if (k == -1 || p[k] == p[j]) {
10 next[++j] = ++k;//即当p[k] == p[j]时,next[j+1] == next[j] + 1=k+1
11 } else {
12 k = next[k];
13 }
14 }
15 return next;
16 }
优化的next:因为当p[j]=p[next[j]]时下一步必然还失配,当这种情况时:零next[j]=next[next[j]];
//优化过后的next数组求法
2 public static int[] getNext(String ps) {
3 char[] p = ps.toCharArray();
4 int[] next = new int[p.length];
5 next[0] = -1;
6 int j = 0;
7 int k = -1;
8 while (j < p.length - 1) {
9 //p[k]表示前缀,p[j]表示后缀
10 if (k == -1 || p[j] == p[k]) {
11 //较之前next数组求法,改动在下面4行
12 if (p[++j] == p[++k]) {
13 next[j]=next[k];// 当两个字符相等时要跳过
14 } else {
15 next[j]=k;//之前只有这一行
16 }
17 } else {
18 k = next[k];
19 }
20 }
21 return next;
22 }