HDU - 6153 A Secret KMP或拓展KMP

本文详细介绍了如何使用拓展KMP算法解决字符串匹配问题,特别是如何计算一个字符串的所有后缀在另一字符串中出现的次数。通过实例讲解了算法原理及实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:点击打开链接

题意就是求一个串T的所有后缀在串S中出现的次数。将S,T都翻转之后就是求T的所有前缀在S中出现的次数。

拓展KMP:可以利用拓展KMP求出S的每一个后缀和T的最长公共前缀。假如当前最长公共前缀为k,就说明长度为k的前缀在S中出现了一次,并且这个k前缀不能构成k+1前缀。用一个cnt数组将各种长度前缀出现的次数记录下来。

来看一下第二个样例。

abababab

aba

首先将样例翻转,得到

babababa

aba

拓展KMP求出的extend数组值如下

extend[0] = 0

extend[1] = 3

extend[2] = 0

extend[3] = 3

extend[4] = 0

extend[5] = 3

extend[6] = 0

extend[7] = 1

所以cnt数组值为

cnt[1] = 1

cnt[2] = 0

cnt[3] = 3

现在我们要根据cnt数组来求T的前缀在S中出现的次数了。

长度为3的前缀:出现了3次不解释。

长度为2的前缀:在长度为3的前缀中出现过3次,不能构成3前缀的2前缀数目(即cnt[2])等于0,所以2前缀出现了3次。

长度为1的前缀:在长度为2的前缀中出现了3次,不能构成2前缀的1前缀数目(即cnt[1])等于1,所以1前缀出现了4次。

ok,解决问题!

KMP:和拓展KMP相似,主要思路就是求出不能构成k + 1前缀的k前缀数目。相信大家都写过用KMP统计T串在S串中出现次数的题目吧。和那个题的代码相似,KMP匹配的时候,我们只需要在失配匹配完成的时候记录一下即可。设失配的时候已经匹配的长度为k,那么这个k前缀不能构成k + 1前缀。

给出KMP的代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long int LL;
const int N = 1000005;
const int MOD = 1000000007;
char S[N], T[N];
int lens, lent;
int Next[N];
int cnt[N];

void getNext()
{
    int i = 0;
    int k = -1;
    Next[0] = -1;
    while (i < lent)
        if (k == -1 || T[i] == T[k])
            Next[++i] = ++k;
        else
            k = Next[k];
}

void KMP_count()
{
    int i = 0;
    int j = 0;
    getNext();
    while (i <= lens) // 注意不能是<,否则最后一个模式会遗漏,用题目中的第二个样例debug一下就知道了。
    {
        if (j == -1 || S[i] == T[j])
        {
            i++;
            j++;
        }
        else // 失配的时候记录一下
        {
            cnt[j]++;
            j = Next[j];
        }
        if (j == lent) // 匹配完成的时候记录一下
        {
            cnt[j]++;
            j = Next[j];
        }
    }
}

int main()
{
    //freopen("test.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    int Case;
    scanf("%d", &Case);
    while (Case--)
    {
        scanf("%s%s", S, T);
        lens = strlen(S);
        lent = strlen(T);
        reverse(S, S + lens);
        reverse(T, T + lent);
        memset(cnt, 0, sizeof(cnt));
        KMP_count();
        LL tmp = cnt[lent];
        LL ans = cnt[lent] * lent;
        for (int i = lent - 1; i >= 1; i--)
        {
            tmp = (tmp + cnt[i]) % MOD;
            ans = (ans + tmp * i) % MOD;
        }
        printf("%I64d\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值