BF,KMP
BF和KMP算法都是针对于字符串的操作,是用于字符串匹配的,从主串中查找有无子串的存在,若匹配成功,返回主串中子串首元素的下标,匹配失败返回-1
BF算法
时间复杂度 :O(mn)
BF算法属于一种暴力破解的算法,穷举所有可能来进行匹配,在其算法过程中存在回溯,这很影响算法效率,因此实际运用情况很少,更多选择其他算法,如KMP
BF算法是通过循环,从主串的第一个元素开始,与子串的第一个元素进行匹配,若匹配成功,则让主串的第二个元素与子串的第二个元素进行匹配,依次类推匹配,若匹配失败,则让主串的下一个元素与子串的首元素重新开始匹配,后面依次类推,直到完全匹配成功,返回下标,或匹配失败,返回-1
int BF(const char *s,const char *sub,int pos)
{
int slen=strlen(s);
int sublen=strlen(sub);
int i=pos;
int j=0;
while(i<slen && j<sublen)
{
if(s[i]==sub[j])
{
i++;
j++;
}
else
{
i=i-j+1;
j=0;
}
}
if(j>=sublen)
{
return i-j;
}
return -1;
}
KMP算法
时间复杂度 :O(m+n)
KMP算法是一种改进的字符串匹配算法,与BF不同的是,在匹配失败的情况下,只有子串回退,而主串不回退,它是利用匹配失败后的信息尽量减少了主串与子串的匹配次数,从而大大提高了算法效率。
KMP的核心就在于求出next数组,该数组保存了子串中每个位置匹配失败时,子串应当回退到什么位置继续与主串进行匹配,这里暂且称某位置匹配失败应该回退的位置下标称作K值,以下图为例,当在红框位置出现匹配失败时,主串不回退,假设子串应回退的位置下标为k,那么k之前的部分与当前主串位置前的相同长度的一部分也应是相等的,如此才有从该位置继续匹配的意义,如紫线部分,因为直到红框才出现匹配失败,可知,绿线部分是相等的,匹配成功的,因此,截取对应段时也该是相等的,我们截取与k前片段相同长度的片段,如黑线部分,推导一下可知,子串中紫线与黑线部分应该是相等的,所以k值应该是匹配失败位置前的片段中,以子串首元素开头,匹配失败位置前一元素为尾的最长的两个真子串的长度,即最长公共前后缀的长度。
k值为next[j]为j下标前 字符串 的最长公共前后缀长度
以下图为例求该字符串的next数组,手动推导可知,子串中0下标前面已经没有元素,匹配失败也仍该是从0下标继续匹配,故0下标的k值为0,但为后面的代码方便,将其设为-1也可,即next[0]=-1;1下标匹配失败时,前面只有一个元素,故也没有最长公共前后缀,所以next[1]=0;这两个值是固定的。
假设子串中匹配到的位置为j,此时 j == 1,k == 0;求2下标即 j+1 的k值。当在j+1位置才匹配失败时,此时分两种情况:
若j下标元素与j位置k值下标元素相等:即j+1前字符串最长公共前后缀长度,相当于 j 前最长公共前后缀长度+1,因为前缀与后缀后面增加的元素相等,所以next[j+1]=next[j]+1即 next[j+1]= j的k + 1,如下图求2下标k值
若 j 下标元素与j位置k值下标元素不相等:如下图求3下标k值时,此时 j == 2,k == 1,此时可类比考虑为,j前字符串前缀与后缀各加上一元素进行匹配,在最后元素位置出现匹配失败,即绿框部分进行字符串匹配在紫框位置匹配失败,子串应进行回退,k下标进行回退,而应该回退的位置我们是知晓的,因为前面元素的next值即K下标的k值我们已经求出来了,故此时 k=next[k];
static void Getnext(const char *sub,int *next)
{
next[0]=-1;
next[1]=0;
int j=1;
int k=0;
int sublen=strlen(sub);
while(j+1<sublen)
{
if(sub[j]==sub[k] || k==-1)
{
next[++j]=++k;
}
else
{
k=next[k];
}
}
}
int KMP(const char *s,const char *sub,int pos)
{
int slen=strlen(s);
int sublen=strlen(sub);
int i=pos;
int j=0;
int *next=(int *)malloc(sizeof(int)*sublen);
Getnext(sub,next);
while(i<slen && j<sublen)
{
if(s[i]==sub[j] || j==-1)
{
i++;
j++;
}
else
{
j=next[j];
}
}
free(next);
if(j>=sublen)
{
return i-j;
}
return -1;
}