A Secret (KMP + 后缀和)

题目链接: A Secret

大致题意:

给出两个字符串, 统计串2的后缀子串在串1中的出现次数. 假设串2中后缀子串长度为i时在串1中出现x次, 则fact(s2, i) = i * x. 最终输出 fact(s2, i)的总和(1 <= i <= |s2|) .

题目感觉不太好叙述, 麻烦自己看吧

解题思路:

对于字符串匹配, 我们很容易想到kmp, 但是kmp的匹配和这个题的思路有些不同, kmp是计算前缀, 而本题则是计算后缀. 所以我们不妨将s1与s2都进行翻转, 这样我们就可以得到kmp的模板串s1, 模式串s2.

现在我们发现, 在我们进行kmp时, 如果匹配到第j位失败(第j-1位成功匹配), 则s2的[1, j - 1]区间的前缀子串匹配的长度都应当+1. 这样我们发现, 我们可以通过后缀和优化, 从而在线性时间内得出结果.

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1E6 + 10, mod = 1E9 + 7;
char s1[N], s2[N];
int ne[N], cou[N]; //起初cou数组为以第i位匹配成功, i+1匹配失败时的情况数量
int main()
{
    int t; cin >> t;
    while (t--) {
        memset(cou, 0, sizeof cou);
        
        scanf("%s %s", s1 + 1, s2 + 1); 
        int l1 = strlen(s1 + 1), l2 = strlen(s2 + 1);
        reverse(s1 + 1, s1 + 1 + l1); reverse(s2 + 1, s2 + 1 + l2); 

        for (int i = 2, j = 0; i <= l2; ++i) { //kmp处理ne数组
            while (j && s2[i] != s2[j + 1]) j = ne[j];
            if (s2[i] == s2[j + 1]) ++j;
            ne[i] = j;
        }
        
        /* KMP */
        int j = 0;
        for (int i = 1; i <= l1; ++i) {
            while (j && s1[i] != s2[j + 1]) cou[j]++, j = ne[j]; //匹配到j成功, j+1失败, 记录情况
            if (s1[i] == s2[j + 1]) ++j;
        }
        while (j) cou[j]++, j = ne[j]; //收尾操作, 很重要

        //求后缀和, 此时cou数组性质改变为串s2中长度为i的前缀子串匹配次数.
        for (int i = l2 - 1; i >= 1; --i) cou[i] += cou[i + 1]; 

        int res = 0; //计算res
        for (int i = 1; i <= l2; ++i) res = (res + 1ll * cou[i] * i) % mod;
        printf("%d\n", res);
    }
    return 0;
}

END

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值