pgsql匹配字符串的前缀和后缀_前缀数组(KMP)超超超超超详解

755785dc36122f88fa6d69498b744aa0.png

KMP算法,由

三位大神共同提出,是一种改进的字符串匹配算法。

对于如“求长度为

的字符串
中在包含多少个长度为
的模式串
”这类问题,相对于从前往后逐字符比较的时间复杂度为
纯暴力算法Brute-Force,优化后的KMP算法可以在
的时间复杂度内实现两个字符串的匹配。

很显然,当我们考虑Brute-Force算法的过程时,我们会发现当在主串中逐字符匹配模式串时,主串中互相重叠的模式串会重复匹配到重叠的部分。

而同样显而易见,重叠的部分对于前面的模式串来说是前面模式串的后缀,对于后面的模式串来说是后面模式串的前缀,所以说“跳过重叠部分”的问题就可以被转换成“预处理字符串前后缀的情况”的问题了。

KMP算法的核心在于一个被称为“失配指针”或者“部分匹配表”的数组

对于给定下标从

开始字符串
,此时
的意义为
个元素的最大的
,使得
个元素
构成的子串
个元素构成
的字符串与
个元素构成
的字符串 相同,且为了方便实现,我们指定

如字符串

对应的
即为:

40f8b39b5a2ea0c878f83526c482e60c.png

遍历字符串

数组时,假设当前遍历到的元素为
,上一位元素
对应的

如果

,由于对于
个元素
构成的子串
,有
个元素和
个元素构成的字符串
相同。所以
个元素构成的字符串在
结尾加上
后,依然与
个元素构成的字符串在
结尾加上
后相同。

所以此时

,成立条件为
,这是第一种情况。

如果

,那么显然对于
个元素构成的子串
结尾加上
后,对应的
必然小于

又因为对于

个元素和
个元素构成的字符串
相同,而
数组记录的又是字符串的前缀后缀相等的
最大值,所以对于
失配的情况,我们要让
取到最大值,我们就要保留
尽可能多的相等的前后缀字符串。

个元素和
个元素构成的字符串是
最大的使得前后缀字符串相等的字符串长度,所以此时继续对于
匹配,若能够满足
那当前就是
第一种情况,若不能满足那么当前就是不能匹配的第二种情况,也就是 重复当前步骤直到出现第一种情况或者当前的
数组匹配到

所以此时对于

,不断重复
直到

部分核心代码如下:
Next[0]=-1;
int j=0,k=-1,len=strlen(str);
while(j<len)
    if(k==-1||str[j]==str[k])
        Next[++j]=++k;//赋值
    else
        k=Next[k];//跳next

显然,我们可以发现在每一次赋值时,如果对应的

,那么在下一次匹配中必然会跳
,所以我们可以直接将当前
赋值为

部分核心代码如下:
Next[0]=-1;
int j=0,k=-1,len=strlen(str);
while(j<len)
    if(k==-1||str[j]==str[k]){
        ++j,++k;
        if(str[j]!=str[k])
            Next[j]=Next[k];
        else
            Next[j]=k;//赋值
    }
    else
        k=Next[k];//跳next

这时我们再从

数组回到整个KMP算法,当我们要将主串
和模式串
进行匹配时,对于我们已经求好的
数组,我们可以以同样的方法进行匹配。

也就是说匹配

的前缀字符串的后缀字符串和
的前缀字符串,这样我们也就可以在失配时保留
尽可能多的已经匹配上的部分了。

匹配答案部分核心代码如下:

//匹配主串str1中包含多少模式串str2
i=0,k=0;//注意模式串此时同样也需要从0开始而非-1
len1=strlen(str1);  
len2=strlen(str2);
while(i<len1){
    if(k==-1||str1[i]==str2[k])
        ++i,++k;
    else 
        k=Next[k];
    if(k==len2){//若主串中包含模式串
        ++ans;
        k=Next[k];
    }
}

例题:

【模板】KMP字符串匹配 - 洛谷​www.luogu.com.cn https://loj.ac/p/103​loj.ac
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值