1.BF算法
即暴力算法,是普通的模式匹配算法
算法思想:
将主串S的第一个字符与子串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符与T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。
普通模式匹配算法,其实现过程没有任何技巧,就是简单粗暴地拿一个串同另一个串中的字符一一比对,得到最终结果。
代码复杂度
最理想的时间复杂度为O(n),即第一次匹配成功,n为子串的长度
最坏情况的时间复杂度O(n*m),即两个串每次匹配,都必须匹配至子串的最末尾才能判断匹配失败,n为子串长度,m为主串长度。
BF算法实现
int BF(const char* str, const char* sub, int pos)//O(n*m)
//pos指从主串中该位置开始进行比较
{
assert(str != NULL && sub != NULL);
if (str == NULL || sub == NULL || pos<0 || pos>strlen(str))
{
return -1;
}
int i = pos;
int j = 0;
int lenstr = strlen(str);
int lensub = strlen(sub);
while (i < lenstr && j < lensub)
{
if (str[i] == sub[j])//相等,往后走
{
i++;
j++;
}
else//不相等,i回退到刚才位置的下一个+1,j回退到0
{
i = i - j + 1;
j = 0;
}
}
//利用子串是否遍历完成,来判断查找是否成功,注意:不能利用主串来判断;
if (j >= lensub)
{
return i - j;
}
else
{
return -1;
}
}
int main()
{
const char* str1 = "ababcabcdabcde";
const char* str2 = "abcd";
printf("%d \n", BF(str1, str2, 6));//9
printf("%d \n", BF(str1, str2, 10));//-1
return 0;
}
2.KMP算法
KMP算法不回退主串的指针i,借助next数组中储存的信息把子串移动到正确的位置继续进行匹配,时间复杂度为O(n+m),m为主串长度,n为子串长度
理解next数组
next 数组各值的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀。例如如果next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀。
也意味着在某个字符失配时,该字符对应的next 值会告诉你下一步匹配中,模式串应该跳到哪个位置(跳到next [j] 的位置)。如果next [j] 等于0或-1,则跳到模式串的开头字符,若next [j] = k 且 k > 0,代表下次匹配跳到j 之前的某个字符,而不是跳到开头,且具体跳过了k 个字符。
求解next数组
计算next数组,只与子串有关
next数组的本质是寻找子串中“相同前后缀的长度”,并且一定是最长的前后缀
首先对于第一个字符,显然不存在比它短的前后缀,所以next直接为0
接着对于前两个字符,同样没有相同的前后缀,所以next为零
对于前三个字符,由于A是公共的前后缀,所以next为1
对于前四个字符,由于AB是共同的前后缀,next为2
对于前五个字符,找不到相同的前后缀,next为0
由此得到整个next数组。
KMP算法实现
static int* GetNext(const char* str)
{
int len = strlen(str);
int* next = (int*)malloc(len * sizeof(int));
next[0] = -1;//不能再退;
next[1] = 0;//j=1,k=0;
int j = 1;
int k = 0;
while (j+1 < len)
{
if ((k==-1)||str[k] == str[j])
{
next[++j] = ++k;
/*next[j + 1] = k + 1;
j = j + 1;
k = k + 1;*///OK
}
else
{
k = next[k];
}
}
return next;
}
int KMP(const char* str, const char* sub, int pos)//O(n*m)
{
assert(str != NULL && sub != NULL);
if (str == NULL || sub == NULL || pos<0 || pos>strlen(str))
{
return -1;
}
int i = pos;
int j = 0;
int lenstr = strlen(str);
int lensub = strlen(sub);
int* next = GetNext(sub);
while (i < lenstr && j < lensub)
{
if ((j==-1)||str[i] == sub[j])//相等,往后走
{
i++;
j++;
}
else//不相等,i不回退,
{
//i = i - j + 1;//i不回退
j = next[j];
}
}
free(next);
//利用子串是否遍历完成,来判断查找是否成功,注意:不能利用主串来判断;
if (j >= lensub)
{
return i - j;
}
else
{
return -1;
}
}
int main()
{
const char* str1 = "ababcabcdabcde";
const char* str2 = "abcd";
printf("%d \n", KMP(str1, str2, 0));//5
return 0;
}