字符串匹配算法

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

由于字符串内容没什么可写的,想到明天集训——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++;
		}else
			j=next[j];
	}
	if(j==m)
		return i-m+1;//匹配成功返回匹配位置
	else
		return -1;//匹配失败返回-1
}

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值