即暴力算法,是普通的模式匹配算法
将主串S的第一个字符与子串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符与T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。
普通模式匹配算法,其实现过程没有任何技巧,就是简单粗暴地拿一个串同另一个串中的字符一一比对,得到最终结果。
最理想的时间复杂度为O(n),即第一次匹配成功,n为子串的长度
最坏情况的时间复杂度O(n*m),即两个串每次匹配,都必须匹配至子串的最末尾才能判断匹配失败,n为子串长度,m为主串长度。
- 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;
- }
KMP算法不回退主串的指针i,借助next数组中储存的信息把子串移动到正确的位置继续进行匹配,时间复杂度为O(n+m),m为主串长度,n为子串长度

next 数组各值的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀。例如如果next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀。
也意味着在某个字符失配时,该字符对应的next 值会告诉你下一步匹配中,模式串应该跳到哪个位置(跳到next [j] 的位置)。如果next [j] 等于0或-1,则跳到模式串的开头字符,若next [j] = k 且 k > 0,代表下次匹配跳到j 之前的某个字符,而不是跳到开头,且具体跳过了k 个字符。
计算next数组,只与子串有关
next数组的本质是寻找子串中“相同前后缀的长度”,并且一定是最长的前后缀
首先对于第一个字符,显然不存在比它短的前后缀,所以next直接为0

接着对于前两个字符,同样没有相同的前后缀,所以next为零

对于前三个字符,由于A是公共的前后缀,所以next为1

对于前四个字符,由于AB是共同的前后缀,next为2

对于前五个字符,找不到相同的前后缀,next为0
由此得到整个next数组。
- 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;
- }