typedef struct
{
char data[MaxSize];
int length; //串长
} SqString;
//SqString 是串的数据结构
//typedef重命名结构体变量,可以用SqString t定义一个结构体。
void GetNext(SqString t,int next[]) //由模式串t求出next值
{
int j,k;
j=0;k=-1;
next[0]=-1;//第一个字符前无字符串,给值-1
while (j<t.length-1)
//因为next数组中j最大为t.length-1,而每一步next数组赋值都是在j++之后
//所以最后一次经过while循环时j为t.length-2
{
if (k==-1 || t.data[j]==t.data[k]) //k为-1或比较的字符相等时
{
j++;k++;
next[j]=k;
//对应字符匹配情况下,s与t指向同步后移
//通过字符串"aaaaab"求next数组过程想一下这一步的意义
//printf("(1) j=%d,k=%d,next[%d]=%d\n",j,k,j,k);
}
else
{
k=next[k];
**//我们现在知道next[k]的值代表的是下标为k的字符前面的字符串最长相等前后缀的长度
//也表示该处字符不匹配时应该回溯到的字符的下标
//这个值给k后又进行while循环判断,此时t.data[k]即指最长相等前缀后一个字符**
//为什么要回退此处进行比较,我们往下接着看。其实原理和上面介绍的KMP原理差不多
//printf("(2) k=%d\n",k);
}
}
}
int KMPIndex(SqString s,SqString t) //KMP算法
{
int next[MaxSize],i=0,j=0;
GetNext(t,next);
while (i<s.length && j<t.length)
{
if (j==-1 || s.data[i]==t.data[j])
{
i++;j++; //i,j各增1
}
else j=next[j]; //i不变,j后退,现在知道为什么这样让子串回退了吧
}
if (j>=t.length)
return(i-t.length); //返回匹配模式串的首字符下标
else
return(-1); //返回不匹配标志
}
顺串:用的多,因为串的插入删除操作用的少;
链串:
BF算法:
i回溯:就是i移动了几格就退回几格,j是移动的格数+1,因此i回溯是i=i-(j-1)=i-j+1再往后退一格就是 i=i-j+2
数据结构KMP算法配图详解(超详细)_哈顿之光的博客-CSDN博客_数据结构kmp算法详解
KMP算法,i指标不回退而是移动j指标
首先的一个概念
一个字符串的前缀,和后缀,最长相等前缀和后缀:
字符串 abcdab
前缀的集合:{a,ab,abc,abcd,abcda}
后缀的集合:{b,ab,dab,cdab,bcdab}
那么最长相等前后缀不就是ab嘛.
(根据上图主串在i=6,j=6的时候出现了不匹配,如果我们想让i回溯,最好就是回溯到i = 4,j=1开始匹配,其实就是最大相等前后缀对齐就行)
现在我们用next[j]去保存当j移动到某一位置的时候它前面的字符的最大相等前后缀
KMP算法中多了一个求数组的过程,多消耗了一点点空间。我们设主串s长度为n,子串t的长度为m。求next数组时时间复杂度为O(m),因后面匹配中主串不回溯,比较次数可记为n,所以KMP算法的总时间复杂度为O(m+n),空间复杂度记为O(m)。