算法进阶指南---0x18(KMP)匹配统计

原题链接

在这里插入图片描述

题解

  1. hash前面已经讲过了,我们再用KMP做一下,对于KMP我们先要了解ne数组:next[i]=j 以i为终点的后缀和从1开始的非平凡前缀最大重合是j
  1. 我们先对B预处理出ne,然后对A进行匹配
 for (int i = 1, j = 0; i <= n; i++) {
    
    
        while (j && a[i] != b[j + 1]) j = ne[j];
        if (a[i] == b[j + 1]) j++;
        cnt[j]++;
    }
  1. 我们知道,对于 j 表示的是B能匹配A以i结尾的最长长度是j ,也就是说,在A中子串A[i-j+1,i] 与 B[1,j] 相同(字符串 A 中,以 i−j+1 为起点的与 B 匹配的长度最小为 j),那么我们就用一个数组cnt[x]来记录满足匹配前缀至少为x的后缀的数量
  1. 我们继续看,对于cnt[j],能匹配,那么cnt[cnt[j]]也一定能和言字符串匹配,那么我们就可以拓扑序累加,计算出所有的cnt数组
  1. 最后 cnt[i] 存的是满足匹配的前缀至少为 x 的后缀数量,而题目中所要求的满足匹配的前缀恰好为 x 的答案的应为匹配的前缀至少为 x 的后缀数量 减去 匹配的前缀至少为 x + 1 的后缀数量,即 cnt[x] - cnt[x + 1](后缀和思想)

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>


using namespace std;
const int N = 2e5 + 10;

int n, m, q, x;
char a[N], b[N];
int ne[N], cnt[N];

int main() {
    
    

    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    cin >> n >> m >> q;
    cin >> a + 1 >> b + 1;

    //对B求ne数组
    for (int i = 2, j = 0; i <= m; i++) {
    
    
        while (j && b[i] != b[j + 1]) j = ne[j];
        if (b[i] == b[j + 1]) j++;
        ne[i] = j;
    }

    for (int i = 1, j = 0; i <= n; i++) {
    
    
        while (j && a[i] != b[j + 1]) j = ne[j];
        if (a[i] == b[j + 1]) j++;
        cnt[j]++;
    }

    //更新cnt
    for (int i = m; i > 0; i--) cnt[ne[i]] += cnt[i];

    while (q--) {
    
    
        cin >> x;
        cout << cnt[x] - cnt[x + 1] << endl;
    }


    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44791484/article/details/114290133