字符串匹配算法

来源:互联网 发布:算法第四版百度云 编辑:程序博客网 时间:2024/05/18 03:26

返校后的第一天集训——字符串。

由于字符串内容没什么可写的,想到明天集训——kmp算法,就一起写字符串匹配的内容。

字符串匹配问题是一个非常经典的问题,给定字符串T和字符串B,判断字符串B是否是字符串T的子串,简单的字符串匹配已经有了非常成熟的算法。

先来说说朴素字符串匹配算法吧。

朴素算法的英文命名为BruteForce,暴力的意思,所谓的朴素算法就是算法分析上常讲的暴力求解方法。这是一种方法,也是一种算法思想,就是不考虑空间时间复杂度,以最简单的看待问题的视角去思考,去解决。

一个最朴素的思路就是从字符串T的第一个字符顺次比较模式串B,不匹配则重新从下一个字符开始匹配,直到文本末尾。

int BruteForce(char *T,char *B)//朴素字符串匹配算法{    int i,j;    for(i=0;i<strlen(T)-strlen(B)+1;i++)    {        for(j=0;j<strlen(B);j++)        {            if(T[i+j]!=B[j])                break;        }        if(j==strlen(B))            return i;//匹配成功返回匹配位置    }    return 0;//匹配失败返回0}

朴素算法时间复杂度为O((n-m+1)*m),因为每次匹配失败后,都会回到原来的匹配起点的下一个字符开始匹配,这些步骤很多情况下,并不是必要的。因此Knuth-Morris-Pratt三人改进朴素算法,简称KMP算法,这就是今天集训队内容。
算法的思想:
相比蛮力算法,KMP 算法预先计算出了一个哈希表,用来指导在匹配过程中匹配失败后尝试下次匹配的起始位置(而不是回到原来起点的下一个字符),以此避免重复的读入和匹配过程。这个哈希表被叫做“部分匹配值表”,它的设计是算法精妙之处。
部分匹配值表
要理解部分匹配值表,就得先了解字符串的前缀(prefix)和后缀(postfix)。
前缀:除字符串最后一个字符以外的所有头部串的组合。
后缀:除字符串第一个字符以外的所有尾部串的组合。
部分匹配值:一个字符串的前缀和后缀中最长共有元素的长度。
以"ABCDABD"为例,
  - "A"的前缀和后缀都为空集,共有元素的长度为0;
  - "AB"的前缀为[A],后缀为[B],共有元素的长度为0;
  - "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;
  - "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;
  - "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;
  - "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;
  - "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。
所以"ABCDABD"的部分匹配表为"0000120";

如何写出部分匹配表的next函数呢。
具体的代码如下:

void getNext(){    int l=strlen(B);    int i=0,j=-1;    next[0]=-1;    while(i<l)    {        if(j==-1||B[i]==B[j])        {            i++;j++;            next[i]=j;        }        else j=next[j];    }}


求next数组的改进算法

void getNextval(){    int i=1,j=0;    next[1]=0;    int l=strlen(B);    while(i<=l)    {        if(j==0 || B[i]==B[j])        {            ++i;++j;            if(B[i]!=B[j]) next[i]=j;            else next[i]=next[j];        }        else j=next[j];    }}



int kmp()//kmp算法{getNext();int n=strlen(T);int m=strlen(B);int i=0,j=0;while(i<n &&j<m){if(j==-1 || T[i]==B[j]){i++; j++;}elsej=next[j];}if(j==m)return i-m+1;//匹配成功返回匹配位置elsereturn -1;//匹配失败返回-1}

KMP算法的时间复杂度为O(n+m),效率比BF算法快多了,但在实际上基本不用KMP算法,因为小数据匹配的话,BF完全够用了,还可以直接用<string.h>头文件中的strstr来匹配;大数据的话,KMP的效率明显不够,还有一种比KMP算法快3-5倍的的BM算法。但是KMP算法是前人解决问题的想法结晶,其核心在于next数组的运用。


0 0