字符串模式匹配算法

1、朴素的模式匹配算法:用两个指针(数组角标)分别指向主串和子串,当字符相等时,指针(角标)都加一,否则,都退回到适当的位置,直到找到子串在主串中出现的位置为止。算法实现如下:

//返回子串t在主串s中第pos个位置的字符开始之后的位置,若不存在,则函数值返回-1
// 0<=pos<=strlen(s)-1
int index(char *s,char *t,int pos)
{
    int len1 = strlen(s);
    int len2 = strlen(t);
    int i = pos;//i从pos位置开始,是主串s的下标
    int j = 0;//j用于子串t中当前位置的下标

    while(i<len1 && j<len2)
    {
        if (s[i] == t[j])
        {
            i++;
            j++;
        }
        else
        {
            //i和j都回退
            i = i-j+1;
            j = 0;
        }
    }
    if (j == len2)
    {
        return i-len2;
    }
    else
    {
        return -1;
    }

}

2、KMP模式匹配算法:它是对朴素模式匹配算法的优化,用两个指针(数组角标)分别指向主串和子串,当字符相等时,指针(角标)都加一,否则,指向主串的指针或角标不变,而指向子串的指针或角标退回到适当地位置(这些位置存放在一个next数组当中),直到找到子串在主串中出现的位置为止。
这个next数组的长度和子串的长度相同,并且只与子串有关。它可由下面的公式表示:
这里写图片描述
j是next数组的下标,当j=0时,next[j]=-1;当j=1时,next[j]=0;在其他情况下,计算子串中当前字符之前的前缀串字符和后缀串字符相等的个数,并且前缀串字符不能包含当前字符的前一个字符,后缀串字符不能包含第一个字符。比如”abcabd”,若当前字符是c,则角标j是2,则前缀字符只有a,后缀字符只有b,又不相等,故next[2]=0,当j指向d时,前缀字符有”ab”,后缀串也有”ab”,故最大长度max=2,即next[j]=2.
计算next数组的代码如下:

**方法一:**
//通过计算返回子串t的next数组
void get_next(char *t,int *next)
{
    int i,j;
    i = 0;
    j = -1;
    next[0] = -1;
    int len = strlen(t);
    while (i < len-1)
    {
        if (j==-1 || t[i]==t[j])//t[i]表示后缀的单个字符,t[j]表示前缀的单个字符
        {
            ++i;
            ++j;
            next[i] = j;
        }
        else
        {
            j = next[j];//表示j回退的位置,若字符不相同,则j值回溯
        }
    }
}
**方法二**
int main()
{
    char str[] = "aabaaaca";
    int next[8];

    next[0] = -1;
    next[1] = 0;

    for (int i=2;i<8;++i)
    {
        int count = next[i-1];
        char *p = str + count;
        char *s = str + i - 1;

        if (*p == *s)
        {
            count++;
        }
        else
        {
            while (count>0)
            {
                p--;
                if (*p != *s)
                {
                    count--;
                }
                else
                {
                    break;
                }
            }
        }
        next[i] = count;
    }
    return 0;
}

然后,KMP算法的实现如下:

//返回子串t在主串s中第pos个位置字符开始之后的位置,若不存在,则函数返回值为-1
//t非空,0<=pos<=strlen(s)-1
int index_kmp(char *s,char *t,int pos)
{
    int len1 = strlen(s);
    int len2 = strlen(t);
    assert(pos>=0 && pos<=len1-1);
    int i = pos;
    int j = 0;
    int next[255];//定义一个next数组
    get_next(t,next);
//  get_nextval(t,next);//对t分析,得到next数组
    while (i<len1 && j<len2)
    {
        if (j==-1 || s[i] == t[j])
        {
            i++;
            j++;
        }
        else
        {
            j = next[j];//j退回到合适的位置,i值不变
        }
    }
    if (j == len2)
    {
        return i-len2;
    }
    else
    {
        return -1;
    }
}

当然,上面的求next数组的方法还可以改进,比如,当主串是”aaaabcde”,子串是”aaaaax”,此时如果按上述算法求出next数组,在字符串匹配过程中会出现较多不必要进行的比较操作,因此要对next函数进行改进。代码如下:

//获取nextval数组,修改的部分加星号表示
void get_nextval(char *t,int *nextval)
{
    int i,j;
    i = 0;
    j = -1;
    nextval[0] = -1;
    int len = strlen(t);
    while (j<len-1)
    {
        if (j==-1 || t[i]==t[j])//t[i]表示后缀字符,t[j]表示前缀字符,i不回退
        {
            i++;
            j++;
            ****if (t[i] != t[j])//当前字符与前缀字符不同
            {
                nextval[i] = j;
            }
            else//当前字符与前缀字符相同
            {
                nextval[i] = nextval[j];
            }****
        }
        else//若前缀字符和后缀字符不相同,j会退到合适的位置
        {
            j = nextval[j];
        }
    }
}

总结改进过得KMP匹配算法,它是在计算出next值得同时,如果i位字符与它next值指向的j位字符相等,则该i位的nextval就指向j位的nextval值,如果不等,则该i位的nextval值就是它自己i的next的值。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值