字符串kmp-boarder

问题描述

wzy给了你一个字符串,请你计算一下这个字符串最多是由多少个相同子串拼成的。

注意:原串 abcdabcd,则 abcd出现两次,则该字符串最多是由两个相同子串拼成的。

输入格式

第一行一个字符串 s 。

输出格式

输出一个数,表示这个字符串最多是由多少个相同子串拼成的。

样例输入

abcdabcd

样例输出

2

解题代码:

#include
using namespace std; 
const int N = 1e6 + 9;
char s[N]; 
int nex[N]; // 定义 next 数组,用于 KMP 算法

main() {
  cin >> s + 1; // 从下标 1 开始读取字符串
  int n = strlen(s + 1); // 计算字符串的长度
  nex[0] = nex[1] = 0; // 初始化 next 数组的前两个值
  for (int i = 2, j = 0; i <= n; i++) { // 构建 next 数组
    while (j && s[i] != s[j + 1]) j = nex[j]; // 如果当前字符不匹配,则回退 j
    if (s[i] == s[j + 1]) j++; // 如果当前字符匹配,则 j 增加 1
    nex[i] = j; // 将当前匹配的长度 j 存入 next 数组
  }
  int t = n - nex[n]; // 计算最小循环节的长度
  if (t != n && n % t == 0) cout << n / t; // 如果字符串由多个相同子串拼成,则输出子串的个数
  else cout << 1; // 否则输出 1
}

1. 如何想到用 KMP 算法的 next 数组来解决这个问题?

这个问题是要求字符串最多由多少个相同子串拼成,而 KMP 算法的 next 数组恰好可以帮助我们找到字符串的循环节。以下是思考过程和方法选择的逻辑:


(1). 问题分析

  • 目标:找到字符串的最多相同子串拼接次数。

  • 关键点

    • 字符串是否由某个子串重复多次构成?

    • 如果是,这个子串的长度是多少?

    • 如何高效地找到这个子串?


(2). KMP 算法的启发

  • KMP 算法的核心是部分匹配表(next 数组),它记录了字符串的每个前缀的最长公共前后缀长度。

  • next 数组的性质

    • 如果字符串由某个子串重复多次构成,那么 next 数组会反映出这种周期性。

    • 具体来说,next[n] 的值与字符串的循环节长度密切相关。


(3). 为什么选择 KMP 算法?

  • 高效性:KMP 算法的时间复杂度是 O(n),适合处理大规模字符串。

  • next 数组的作用

    • next[n] 表示字符串的最长公共前后缀长度。

    • 通过 next[n],我们可以推导出字符串的最小循环节长度。


(4). 如何想到用 t=n−next[n]?

  • 观察周期性

    • 如果字符串由某个子串重复多次构成,那么它的最长公共前后缀长度 next[n] 会接近字符串长度 n。

    • 去掉这个公共前后缀后,剩下的部分就是循环节。

  • 公式推导

    • 循环节长度 t=n−next[n]。

    • 如果 t≠n且 n%t==0,则字符串由 n/t 个相同子串拼成。

2. 什么是循环节?

  • 循环节是指字符串中最小的重复单元

  • 例如,字符串 ababab 的循环节是 ab,因为它可以看作是 ab 重复 3 次。


3. 如何找到循环节?

  • 通过 KMP 算法的 next 数组,我们可以找到字符串的最长公共前后缀长度

  • 最小循环节的长度 tt 可以通过公式计算:

    其中:

    • n 是字符串的长度。

    • next[n]是字符串的最长公共前后缀长度。


4. 为什么 t=n−next[n] 是最小循环节长度?

  • 最长公共前后缀next[n] 表示字符串的前 n 个字符中,最长的既是前缀又是后缀的子串长度。

  • 循环节的意义:如果字符串由循环节重复构成,那么去掉这个最长公共前后缀后,剩下的部分就是循环节。

  • 例如:

    • 字符串 ababab

      • 最长公共前后缀是 abab(长度为 4)。

      • 循环节长度 t=6−4=2,即 ab


5. 为什么 t≠n且 n%t==0 时,字符串由 n/t个相同子串拼成?

  • t≠n

    • 如果 t=n,说明字符串没有循环节,整个字符串是一个整体,不能由更小的子串重复构成。

    • 例如,字符串 abcde 的循环节长度是 5,等于字符串长度,因此它不能由多个相同子串拼成。

  • n%t==0

    • 如果 n%t==0,说明字符串的长度 n 是循环节长度 t 的整数倍。

    • 这意味着字符串可以完整地由 n/t 个循环节拼接而成。

    • 例如:

      • 字符串 ababab

        • 循环节长度 t=2。

        • 字符串长度 n=6。

        • 6%2==0,因此字符串由 6/2=3 个 ab 拼接而成。

你可能感兴趣的:(算法)