BF算法与KMP算法

针对串的操作,在主串s里面查找子串sub,从pos位置开始的第一个符合的子串,返回第一个字符的下标。
BF算法如下:
时间复杂度:O(mn)
当字符相等时,j++,i++,当不等时,j需要回退到0号下标,i需要回退到之前的位置+1

int BF(const char *s,const char *sub, int pos)
//在s里面查找子串sub,从pos位置开始的第一个符合的子串,返回第一个字符的下标
{
    assert(sub != NULL);
    assert(s != NULL);
    int lens = strlen(s);
    int lensub = strlen(sub);
    if (pos<0 || pos>lens)
    {
        return -1;
    }
    int i = pos;//ipos开始走主串
    int j = 0;//j0开始走子串
    while (i < lens && j < lensub)
    {
        if (s[i] == sub[j])//当字符相同时,继续走
        {
            i++;
            j++;
        }
        else//当字符不同时,i退回到次字符,j退回0
        {
            i = i - j + 1;
            j = 0;
        }
    }
    if (j >= lensub)
    {
        return i - j;//当j遍历完子串时,返回下标[i-j]
    }
    else
    {
        return -1;
    }
}

KMP算法:与BF算法不同的是,相对于 BF 算法来说KMP 算法更为高效,原因在于 BF 算法的时间复杂度是:O(mn)M 代表主串的长度,n 代表子串的长度。而KMP 的话,时间复杂度就变为 O(m+n);KMP 和 BF 唯一不一样的地方在,主串的i 并不会回退,并且 j 也不会移动到 0 号位置。但在这里j需要回退到k位置,于是引出了存放串的各个位置对应的k值的一个next[]数组,也就是用 next[j] = k来表示,不同的 j 来对应一个K 值, 这个K 就是你将来要移动的j 要移动的位置。而K 的值是这样求的:
1、规则:找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 开始,另一个以 j-1 下标结尾。
2、不管什么数据 next[0] = -1;next[1] = 0;在这里,我们以下标来开始,而说到的第几个第几个是从 1 开始;
-1 的理由:当主串为–”defrdes” 子串为:”abc” 一开始就匹配失败。
0 的理由:当子串在 1 号下标匹配,此时为 0;
next 数组的优化,即如何得到 nextval 数组:
有如下串:aaaaaaaab,他的 next 数组是-1,0,1,2,3,4,5,6,7.而修正后的数组 nextval 是:
-1,-1,-1,-1,-1,-1,-1,-1,7。为什么出现修正后的数组,假设在 5 号处失败了,那退一步还是 a,还是相等, 接着退还是 a。
例:
串 : a b c a a b b c a b c a a b d a b
next: -1 0 0 0 1 1 2 0 0 1 2 3 4 5 6 0 1
nextval:-1 0 0 -1 1 0 2 0 -1 0 0 -1 1 0 6 -1 0
黑体a的nextval 求法:a的next为4,而4号下标的值也为a,所以nextval就不为黑体a的next值,而为4号下标a的next值,即为1。

//next数组:保存匹配失败后。需要回退的位置
//找到相同的两个真子串 
//一个以0号下标开始,一个是以j-1号下标结尾
void GetNext(int *next,const char *sub)//求子串next数组的算法
{
    assert(sub != NULL);
    int lensub = strlen(sub);
    next[0] = -1;
    next[1] = 0;
    int i = 2;//第二项
    int k = 0;//第二项下标对应的k值
    while (i < lensub)//例:abcababcabc-1 0 0 0 1 2 1 2 3 4 5 a的next数组值即第一个a的k值为-1,第一个b的为0,继续走。。。
    {
        if ((k == -1) || sub[k] == sub[i - 1])//当第一个b的k值作字符下标所对应的字符 a不等于b时,else
        {
            next[i] = k + 1;//**要求的字符的k值,给k+1,则第三个字符c的k值为-1+1=0
            i++;
            k++;
        }
        else
        {
            k = next[k];//**next[0]=-1付给k,k=-1进入if()中
        }
    }
}

int KMP(const char *s,const char *sub, int pos)
{
    int lens = strlen(s);
    int lensub = strlen(sub);
    int *next = (int *)malloc(sizeof(int)*(lensub+1));//开辟next[]数组并不是子串的长度,在子串长度上加1
    assert(next != NULL);
    GetNext(next,sub);//调用next函数,得到子串的next数组
    if (pos<0 || pos>lens)
    {
        return -1;
    }
    int i = pos;//ipos开始走主串
    int j = 0;//j0开始走子串
    while (i < lens && j < lensub)
    {
        if ((j == -1) || s[i] == sub[j])
        {
            i++;
            j++;
        }
        else
        {  
            j = next[j];//i不往回走,j往回走到next对应下标位置
        }
    }
        free(next);
        if (j >= lensub)
        {
            return i - j;//当j遍历完子串时,返回下标[i-j]
        }
        else
        {
            return -1;
        }
}

猜你喜欢

转载自blog.csdn.net/wtzdedaima/article/details/80672077