c语言,这个kmp算法有问题吗,运行后死循环

c语言,这个kmp算法有问题吗,运行后死循环

void GetNext(int* next,char* sub,int subl){  //next[i]记录了sub[i]前,不包括自己的最长公共前后缀
    next[0]=0;
    int i=0;
    int j=1;
    int l=0;
    while(i<subl-1){
        if(sub[i]==sub[j]){
            i++;
            j++;
            l++;
            next[j]=l;
        }
        else if(i==0){
            next[j]=0;
            j++;
        }
        else{
            i=next[i];
            l=next[i];
        }
    }
}

int KMP1(char* str,char* sub,int strl,int subl){  //没用到nextval数组
    int* next=(int*)malloc(sizeof(int)*subl);
    GetNext(next,sub,subl);
    int i,j;
    i=j=0;
    while(i<strl&&j<subl){
        if(str[i]==str[j]){
            i++;
            j++;
        }
        else{
            j=next[j];
        }
    }
    free(next);
    if(j==subl){
        return i-subl;
    }
    return -1;
}

在这个 KMP 算法中,当 sub 中第一个字符与 str 中的某个字符不匹配时,会将 j 更新为 next[j],但是在某些情况下,next[j] 可能会更新为 j,导致出现死循环的情况。具体来说,当 sub 中存在连续相同的字符时,next[j] 就会被更新为 j,从而导致死循环。

为了解决这个问题,你可以将 GetNext() 函数中的代码做如下修改:

void GetNext(int* next,char* sub,int subl){
    next[0]=0;
    int i=0;
    int j=1;
    int l=0;
    while(j<subl){
        if(sub[i]==sub[j]){
            i++;
            j++;
            l++;
            next[j]=l;
        }
        else if(i==0){
            next[j]=0;
            j++;
        }
        else{
            i=next[i];
            l=next[i];
        }
    }
}

这样修改后,当 sub 中存在连续相同的字符时,next[j] 仍然会被更新为 j,但是此时 i 的值也会被更新为 j,从而避免了死循环的情况。

另外,你在 KMP1() 函数中使用了 malloc() 分配内存,但是没有在函数结束后释放内存,这可能会导致内存泄漏。为了避免这个问题,你可以在函数结束后使用 free() 释放 next 数组的内存,例如:

int KMP1(char* str,char* sub,int strl,int subl){
    int* next=(int*)malloc(sizeof(int)*subl);
    GetNext(next,sub,subl);
    int i,j;
    i=j=0;
    while(i<strl&&j<subl){
        if(str[i]==str[j]){
            i++;
            j++;
        }
        else{
            j=next[j];
        }
    }
    int result = -1;
    if(j==subl){
        result = i-subl;
    }
    free(next);
    return result;
}

这样,就避免了内存泄漏的问题。

以下引用GPT的回答:

void GetNext(int* next, char* sub, int subl) {
    next[0] = -1;
    int i = 0;
    int j = -1;
    while (i < subl - 1) {
        if (j == -1 || sub[i] == sub[j]) {
            i++;
            j++;
            next[i] = j;
        }
        else {
            j = next[j];
        }
    }
}

int KMP1(char* str, char* sub, int strl, int subl) {
    int* next = (int*)malloc(sizeof(int) * subl);
    GetNext(next, sub, subl);
    int i = 0;
    int j = 0;
    while (i < strl && j < subl) {
        if (j == -1 || str[i] == sub[j]) {
            i++;
            j++;
        }
        else {
            j = next[j];
        }
    }
    free(next);
    if (j == subl) {
        return i - subl;
    }
    return -1;
}

GetNext函数中,我修改了next[0]的初始化为-1。在else块中,删除了else if(i==0)的条件,并将j更新为next[j],然后继续循环。这确保了算法的正确执行,避免了潜在的无限循环问题。

KMP1函数中,将ij的初始化修正为0。增加了条件j == -1if语句中,确保在j处于模式串的起始位置时算法能够正确执行。

这些修改应该解决了你遇到的死循环问题。

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 帮你找了个相似的问题, 你可以看下: https://ask.csdn.net/questions/7525206
  • 你也可以参考下这篇文章:综合性实验:利用KMP算法求子串在主串中出现的次数
  • 除此之外, 这篇博客: C语言KMP算法与字符串朴素的模式匹配算法(暴力)编程小白必学中的 部分也许能够解决你的问题, 你可以仔细阅读以下内容或者直接跳转源博客中阅读:

    KMP算法最关键的地方就是得到部分匹配表(也就是next数组),通过我们计算出的部分匹配表就可以计算出当匹配失败时,j需要回溯到哪里。用代码实现求next数组有个十分精妙的算法。首先,设定next数组的第一个元素next[0]为-1。定义两个变量i=-1、j=0。如果i==-1或第i个和第j个相等时,next[++j]=++i;否则的话将i置为-1;这个过程就是前后缀的比较过程,举个例子,当前后缀的最大公共长度为1时,i与j往后移一位,如果i与j相等那么这时的最大公共元素长度就是2,否则匹配失败,此时i与j不后移,将i置为-1。等于终端需要重新匹配,然后再次进入循环,判断当i==-1,此时i等于-1,因为j所在处匹配失败,所以此时匹配值为i++为0。第一次看代码可能看不懂,需要多看几遍代码方能理解为何要将next[0]设为-1。

    循环结束后,将next[0]又置为0,因为下标为0的元素是串中的第一个元素。之前无字符串,所以部分匹配值为0。循环开始时设定next数组的第一个元素next[0]为-1,只是为了方便计算部分匹配值表。计算完成后,next的每一个元素下标都对应子串T中的每一个元素的下标。同样的子串中下标为j的元素,在next数组里面对应的部分匹配值,就是next[j]。举个例子,当在子串T下标为j处匹配失败时,根据KMP算法我们需要j后移,此时j对应的部分匹配值为next[j],所以j需要后移的位数是j-next

    void GetNext(char* t,int* next,int lent)
    {
    	int i, j;
    	j = 0;
    	i = -1;
    	next[0] = -1;	//设定第一个值为-1。
    	while (j<lent)
    	{
    		if (i == -1 || t[i] == t[j])
    		{
    			next[++j] = (++i);
    		}
    		else
    			i = next[i];
    	}
    	next[0] = 0;	//将j=0时,的部分匹配值改为0。
    }
    
    int Index_KMP(char* s, char* t, int lens, int lent, int* beginpos)
    {
    	int i, j;
    	i = *beginpos;
    	j = 0;
    	int next[100];
    	GetNext(t,next,lent);
    	while (i<lens&&j<lent)
    	{
    	
    		if (s[i] == t[j])
    		{
    			i++;
    			j++;
    		}
    		else
    			j -= j - next[j];		//由后移位数=已匹配串长(j)-部分匹配值next[j],计算出j移到哪里去。
    
    		if (j==0&&s[i]!=t[j])		//由于j回溯到0,如果不进行操作的话会陷入死循环,故当因匹配失败后j回溯到0后,需要将主串的当前位置下标i向后移一位开始新的匹配。
    		{
    			i++;
    		}
    	}
    	if (j == lent)
    	{
    		*beginpos = i - j + 1 + 1;
    		return i - j;
    	}
    	else
    		return -1;
    }

    KMP算法与朴素的模式匹配算法不同的地方在于:KMP算法不需要主串当前位置下标i回溯,i只管前进,而需要根据部分匹配值来确定j需要回溯到哪。在有些匹配问题上,性能明显好于朴素的模式匹配算法。

    代码中将有些关键步骤进行了注释,若还有不懂的地方,请留言。将会进行回复。

    打字不易,请多多点赞。

     

     

     

     

     

  • 您还可以看一下 孙玖祥老师的图解数据结构与算法课程中的 KMP算法思想-动画演示小节, 巩固相关知识点
  • 以下回答来自chatgpt:

    KMP算法实现中出现死循环的问题很可能是在匹配失败情况下没有正确更新匹配位置导致的。在KMP算法中,我们会预处理模式串,得到一个前缀表(prefix table),用来在匹配失败时决定下一步匹配的位置,以避免不必要的字符比较。

    下面是代码中可能出现问题的地方:

    int KMP(char *text, char *pattern) {
        // KMP算法实现
        // ...
    }
    

    这部分代码需要实现KMP算法,并返回在文本中找到匹配模式的起始位置。

    以下是解决方案:

    #include<stdio.h>
    #include<string.h>
    
    void computeLPSArray(char *pattern, int M, int *lps) {
        int len = 0;
        int i = 1;
        lps[0] = 0;
    
        while (i < M) {
            if (pattern[i] == pattern[len]) {
                len++;
                lps[i] = len;
                i++;
            }
            else {
                if (len != 0) {
                    len = lps[len - 1];
                }
                else {
                    lps[i] = 0;
                    i++;
                }
            }
        }
    }
    
    int KMP(char *text, char *pattern) {
        int M = strlen(pattern);
        int N = strlen(text);
    
        int lps[M];
        computeLPSArray(pattern, M, lps);
    
        int i = 0; // 在text中的当前位置
        int j = 0; // 在pattern中的当前位置
    
        while (i < N) {
            if (pattern[j] == text[i]) {
                j++;
                i++;
            }
    
            if (j == M) {
                return i - j;
            }
    
            if (i < N && pattern[j] != text[i]) {
                if (j != 0)
                    j = lps[j - 1];
                else
                    i++;
            }
        }
    
        return -1;
    }
    
    int main() {
        char text[] = "ABABDABACDABABCABAB";
        char pattern[] = "ABABCABAB";
        int result = KMP(text, pattern);
        printf("在文本中找到匹配模式的起始位置: %d\n", result);
        return 0;
    }
    

    上面的代码通过调用computeLPSArray函数来计算模式串的前缀表,并在KMP函数中使用该表来比较文本中的字符和模式串中的字符。如果遇到不匹配的情况,会根据前缀表的值来更新匹配的位置,避免了不必要的字符比较。

    希望上述代码能够解决你的问题,如果还有其他问题,请随时提问。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

谢谢,后面解决了