字符串模式匹配算法

来源:互联网 发布:mac网络诊断isp失败 编辑:程序博客网 时间:2024/05/16 04:36

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

//返回子串t在主串s中第pos个位置的字符开始之后的位置,若不存在,则函数值返回-1// 0<=pos<=strlen(s)-1int 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)-1int 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的值。

原创粉丝点击