📜  门|门CS 2013 |第 49 题(1)

📅  最后修改于: 2023-12-03 14:58:37.533000             🧑  作者: Mango

题目介绍

本题为门 | 门CS 2013 | 第49题。题目难度为普及+/提高,需要具有一定的数据结构和动态规划基础。

题目描述

给定两个字符串 $S$ 和 $T$,求出 $S$ 中有多少个子串与 $T$ 相同。

输入格式

输入文件包含两行,第一行为字符串 $S$,第二行为字符串 $T$。

输出格式

输出文件包含一个整数,即 $S$ 中有多少个子串与 $T$ 相同。

样例输入1
helloello
ello
样例输出1
2
样例输入2
hello
ab
样例输出2
0

解题思路

本题主要考察的是字符串匹配算法。常用的字符串匹配算法包括暴力枚举算法、KMP算法、Boyer-Moore算法等。这里给出两种解题思路。

解法1:暴力枚举

对于每个字符 $S[i]$,从 $i$ 开始,逐一比较 $S[i]$ 和 $T[j]$ 是否相等,如果相等则继续比较下一个字符,直到 $T$ 中的所有字符都匹配上了,则说明找到了一个子串与 $T$ 相同。

时间复杂度为 $O(n^2)$,空间复杂度为 $O(1)$。

解法2:KMP算法

KMP算法是一种高效的字符串匹配算法,用于在一个长文本串 $S$ 中,查找一个模式串 $T$ 的出现位置。

具体算法流程如下:

  1. 根据模式串 $T$,计算出前缀表 $next$ 数组。$next[i]$ 表示模式串 $T[0...i]$ 的最长公共前后缀的长度。

  2. 对于字符串 $S$,从左到右依次比较字符。当字符不匹配时,根据 $next$ 数组将模式串向右移动一定距离。具体做法是将模式串向右移动 $i - next[i]$ 个字符。

时间复杂度为 $O(n)$,空间复杂度为 $O(m)$($m$ 为模式串长度)。

参考代码

解法1:暴力枚举
#include <iostream>
#include <cstring>

using namespace std;

int main() {
    string s, t;
    cin >> s >> t;

    int cnt = 0;
    for (int i = 0; i < s.size(); i++) {
        int j = 0;
        while (j < t.size() && i+j < s.size() && s[i+j] == t[j])
            j++;
        if (j == t.size())
            cnt++;
    }

    cout << cnt << endl;

    return 0;
}
解法2:KMP算法
#include <iostream>
#include <cstring>

using namespace std;

void getNext(string t, int next[]) {
    int j = 0, k = -1;
    next[0] = -1;

    while (j < t.size() - 1) {
        if (k == -1 || t[j] == t[k]) {
            j++;
            k++;
            next[j] = k;
        } else {
            k = next[k];
        }
    }
}

int kmp(string s, string t) {
    int next[t.size()];
    getNext(t, next);

    int i = 0, j = 0, cnt = 0;
    while (i < (int)s.size()) {
        if (j == -1 || s[i] == t[j]) {
            i++;
            j++;
        } else {
            j = next[j];
        }
        if (j == (int)t.size()) {
            cnt++;
            j = next[j];
        }
    }

    return cnt;
}

int main() {
    string s, t;
    cin >> s >> t;

    cout << kmp(s, t) << endl;

    return 0;
}