LeetCode 459. 重复的子字符串

作者:Best_Jerry日期:2026/7/9

leetcode.cn/problems/re…

programmercarl.com/0459.%E9%87…

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

示例 1:

1输入: s = "abab"
2输出: true
3解释: 可由子串 "ab" 重复两次构成。
4

示例 2:

1输入: s = "aba"
2输出: false
3

示例 3:

1输入: s = "abcabcabcabc"
2输出: true
3解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)
4

提示:

  • 1 <= s.length <= 104
  • s 由小写英文字母组成

以下使用KMP方式讲解,强烈建议大家先把以下两个视频看了,理解KMP算法,再来看下面讲解,否则会很懵。

在一个串中查找是否出现过另一个串,这是KMP的看家本领。那么寻找重复子串怎么也涉及到KMP算法了呢?

KMP算法中next数组为什么遇到字符不匹配的时候可以找到上一个匹配过的位置继续匹配,靠的是有计算好的前缀表。

前缀表里,统计了各个位置为终点字符串的最长相同前后缀的长度。

那么 最长相同前后缀和重复子串的关系又有什么关系呢。

可能很多录友又忘了 前缀和后缀的定义,再回顾一下:

  • 前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;
  • 后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串

充分性证明

如果一个字符串s是由重复子串组成,那么 最长相等前后缀不包含的子串一定是字符串s的最小重复子串。

如果s 是由最小重复子串p组成,即 s = n * p

那么相同前后缀可以是这样:

也可以是这样:

最长的相等前后缀,也就是这样:

这里有录友就想:如果字符串s 是由最小重复子串p组成,最长相等前后缀就不能更长一些? 例如这样:

如果这样的话,因为前后缀要相同,所以 p2 = p1,p3 = p2,如图:

p2 = p1,p3 = p2 即: p1 = p2 = p3

说明 p = p1 * 3。

这样p 就不是最小重复子串了,不符合我们定义的条件。

所以,如果这个字符串s是由重复子串组成,那么最长相等前后缀不包含的子串是字符串s的最小重复子串

必要性证明

以上是充分性证明,以下是必要性证明:

如果 最长相等前后缀不包含的子串是字符串s的最小重复子串, 那么字符串s一定由重复子串组成吗

最长相等前后缀不包含的子串已经是字符串s的最小重复子串,那么字符串s一定由重复子串组成,这个不需要证明了。

关键是要证明:最长相等前后缀不包含的子串什么时候才是字符串s的最小重复子串呢。

情况一, 最长相等前后缀不包含的子串的长度 比 字符串s的一半的长度还大,那一定不是字符串s的重复子串,如图:

图中:前后缀不包含的子串的长度 大于 字符串s的长度的 二分之一


情况二,最长相等前后缀不包含的子串的长度 可以被 字符串s的长度整除,如图:

步骤一:因为 这是相等的前缀和后缀,t[0] 与 k[0]相同, t[1] 与 k[1]相同,所以 s[0] 一定和 s[2]相同,s[1] 一定和 s[3]相同,即:,s[0]s[1]与s[2]s[3]相同 。

步骤二: 因为在同一个字符串位置,所以 t[2] 与 k[0]相同,t[3] 与 k[1]相同。

步骤三: 因为 这是相等的前缀和后缀,t[2] 与 k[2]相同 ,t[3]与k[3] 相同,所以,s[2]一定和s[4]相同,s[3]一定和s[5]相同,即:s[2]s[3] 与 s[4]s[5]相同。

步骤四:循环往复。

所以字符串s,s[0]s[1]与s[2]s[3]相同, s[2]s[3] 与 s[4]s[5]相同,s[4]s[5] 与 s[6]s[7] 相同。

可以推出,在由重复子串组成的字符串中,最长相等前后缀不包含的子串就是最小重复子串。

即 s[0]s[1] 是最小重复子串

以上推导中,录友可能想,你怎么知道 s[0] 和 s[1] 就不相同呢? s[0] 为什么就不能是最小重复子串。

如果 s[0] 和 s[1] 也相同,同时 s[0]s[1]与s[2]s[3]相同,s[2]s[3] 与 s[4]s[5]相同,s[4]s[5] 与 s[6]s[7] 相同,那么这个字符串就是有一个字符构成的字符串。

那么它的最长相同前后缀,就不是上图中的前后缀,而是这样的的前后缀:

录友可能再问,由一个字符组成的字符串,最长相等前后缀凭什么就是这样的。

有这种疑惑的录友,就是还不知道 最长相等前后缀 是怎么算的。

可以看这里:KMP讲解 (opens new window),再去回顾一下。

或者说,自己举个例子,aaaaaa,这个字符串,他的最长相等前后缀是什么?

同上以上推导,最长相等前后缀不包含的子串的长度只要被 字符串s的长度整除,最长相等前后缀不包含的子串一定是最小重复子串。


情况三,最长相等前后缀不包含的子串的长度 不被 字符串s的长度整除得情况,如图:

步骤一:因为 这是相等的前缀和后缀,t[0] 与 k[0]相同, t[1] 与 k[1]相同,t[2] 与 k[2]相同。

所以 s[0] 与 s[3]相同,s[1] 与 s[4]相同,s[2] 与s[5],即:,s[0]s[1]与s[2]s[3]相同 。

步骤二: 因为在同一个字符串位置,所以 t[3] 与 k[0]相同,t[4] 与 k[1]相同。

步骤三: 因为 这是相等的前缀和后缀,t[3] 与 k[3]相同 ,t[4]与k[5] 相同,所以,s[3]一定和s[6]相同,s[4]一定和s[7]相同,即:s[3]s[4] 与 s[6]s[7]相同。

以上推导,可以得出 s[0],s[1],s[2] 与 s[3],s[4],s[5] 相同,s[3]s[4] 与 s[6]s[7]相同。

那么 最长相等前后缀不包含的子串的长度 不被 字符串s的长度整除 ,最长相等前后缀不包含的子串就不是s的重复子串


充分条件:如果字符串s是由重复子串组成,那么 最长相等前后缀不包含的子串 一定是 s的最小重复子串。

必要条件:如果字符串s的最长相等前后缀不包含的子串 是 s最小重复子串,那么 s是由重复子串组成。

在必要条件,这个是 显而易见的,都已经假设 最长相等前后缀不包含的子串 是 s的最小重复子串了,那s必然是重复子串。

关键是需要证明, 字符串s的最长相等前后缀不包含的子串 什么时候才是 s最小重复子串

同上我们证明了,当 最长相等前后缀不包含的子串的长度 可以被 字符串s的长度整除,那么不包含的子串 就是s的最小重复子串。


代码分析

next 数组记录的就是最长相同前后缀( 字符串:KMP算法精讲 (opens new window)), 如果 next[len - 1] != -1,则说明字符串有最长相同的前后缀(就是字符串里的前缀子串和后缀子串相同的最长长度)。

最长相等前后缀的长度为:next[len - 1] + 1。(这里的next数组是以统一减一的方式计算的,因此需要+1,两种计算next数组的具体区别看这里:字符串:KMP算法精讲 (opens new window))

数组长度为:len。

len - (next[len - 1] + 1) 是最长相等前后缀不包含的子串的长度。

如果len % (len - (next[len - 1] + 1)) == 0 ,则说明数组的长度正好可以被 最长相等前后缀不包含的子串的长度 整除 ,说明该字符串有重复的子字符串。

打印数组

强烈建议大家把next数组打印出来,看看next数组里的规律,有助于理解KMP算法

如图:

next[len - 1] = 7next[len - 1] + 1 = 8,8就是此时字符串asdfasdfasdf的最长相同前后缀的长度。

(len - (next[len - 1] + 1)) 也就是: 12(字符串的长度) - 8(最长公共前后缀的长度) = 4, 为最长相同前后缀不包含的子串长度

4可以被 12(字符串的长度) 整除,所以说明有重复的子字符串(asdf)。

1class Solution {
2    /*
3    * 充分条件:如果字符串s是由重复子串组成的,那么它的最长相等前后缀不包含的子串一定是s的最小重复子串。
4    * 必要条件:如果字符串s的最长相等前后缀不包含的子串是s的最小重复子串,那么s必然是由重复子串组成的。
5    * 推得:当字符串s的长度可以被其最长相等前后缀不包含的子串的长度整除时,不包含的子串就是s的最小重复子串。
6    *
7    * 时间复杂度:O(n)
8    * 空间复杂度:O(n)
9    */
10    fun repeatedSubstringPattern(s: String): Boolean {
11        val length = s.length
12        // Step 1.构建KMP算法的前缀表
13        // 前缀表的值表示 以该位置结尾的字符串的最长相等前后缀的长度
14        val next = Array(size = length) { 0 }
15        var j = 0
16        for (i in 1..<length) {
17            // 只要前缀后缀还不一致,就根据前缀表回退j直到起点为止
18            while (j > 0 && s[i] != s[j]) {
19                j = next[j - 1]
20            }
21            if (s[i] == s[j]) {
22                j++
23            }
24            next[i] = j
25        }
26        // Step 2.判断重复子字符串
27        // 当字符串s的长度可以被其最长相等前后缀不包含的子串的长度整除时
28        // 不包含的子串就是s的最小重复子串
29        return next[length - 1] > 0 && length % (length - next[length - 1]) == 0
30    }
31}
32

LeetCode 459. 重复的子字符串》 是转载文章,点击查看原文


相关推荐


HDFS javaAPI-windows的IDEA中java文件在linux中的hadoop平台运行
chde2Wang2026/7/1

目录 运行前提 一、在IDEA的Maven项目中创建MkDirDemo类 (1)先确认目录结构(Maven 标准目录,必须按这个来) (2)hdfs java操作代码输入 (3)验证pom.xml文件中hadoop依赖是否和linux中hadoop版本一致 (4)确认 HDFS RPC 地址(关键) 二、Windows 本地配置 Hadoop 运行环境(必做,否则报 winutils 缺失) (1)下载 Windows 适配 hadoop 二进制包Hadoop3.x 下载对应


Obsidian - 使用 Share Note 分享笔记并自部署
LinXunFeng2026/6/22

欢迎关注微信公众号:FSA全栈行动 👋 一、前言 最近在整理 Obsidian 笔记时,经常会遇到一个小需求:想把某一篇笔记快速分享给别人,但又不想为了它单独搭建博客、导出 PDF 或复制到其它平台。 如果只是分享纯文本,复制粘贴当然可以。但 Obsidian 笔记里往往会有这些内容: 图片附件 代码块 Callout 提示块 标签、任务列表 Dataview 查询结果 当前主题样式和自定义 CSS 笔记之间的内部链接 这时候手动搬运就有点麻烦了,格式容易丢,图片也要重新处理。这里介绍一


RabbitMQ 从入门到精通:Spring Boot 实战三部曲(一)—— 基础核心与快速上手
绝知此事2026/6/14

RabbitMQ 从入门到精通:Spring Boot 实战三部曲(一)—— 基础核心与快速上手 专题导读:本系列共三篇,从基础到高级,带你系统掌握 RabbitMQ 在 Spring Boot 项目中的实战应用。 第一篇:基础核心与快速上手(本文)第二篇:进阶特性与可靠性保障第三篇:高级应用与性能优化 📖 前言 在当今的分布式系统中,消息队列已成为不可或缺的基础设施。RabbitMQ 作为最流行的消息中间件之一,以其可靠性、灵活性和易用性著称。 本文将从 RabbitMQ 的基础概念出


iOS、Android、Flutter 2026 流行框架对比
王若风2026/6/7

参考文章:iOS、Android、Flutter 流行框架对比(原始链接) 我把我 2 年前的一篇博客文章进行了一次“重写”,于是有了这篇。 原文的选题很实用,结构也很清晰:按布局、网络请求、图片加载三个维度,把 iOS、Android、Flutter 常见框架放在一起横向看。 帮助移动端开发洞察各端核心框架的流行趋势,提供洞察和选项参考。 但原文的数据,放到 2026 年 6 月再看,已经有点过期了。 比如 Jetpack Compose 已经不是“新趋势”,而是 Android 新项目的


数据采集卡技术全解:从硬件架构到行业应用
zlinear数据采集卡2026/5/31

目录 数据采集卡技术全解:从硬件架构到行业应用 一、数据采集卡基础概念与分类体系 1.1 核心概念:连接物理世界与数字世界的桥梁 1.2 数据采集卡的核心功能构成 1.3 与传统测量仪器的本质区别 1.4 多维度的分类体系 1.4.1 按核心性能(采样率)分类 1.4.2 按总线接口类型分类 1.4.3 按功能与应用分类 章节小结 二、硬件架构深度解析 2.1 整体架构视图:从信号入口到数据出口 2.2 模拟前端:信号的“守门人”与“化妆师” 2.3 数据转换核心:A


技术选型决策树:什么团队、什么项目该选什么框架 | 跨平台框架深度对决(4)
陆业聪2026/5/10

跨平台框架深度对决系列 · 第4/4篇(完结篇) Flutter vs KMP vs KuiKly vs RN,谁是2026年的最优解 第1篇:跨平台框架全景图——Flutter/KMP/KuiKly/RN的2026年格局 第2篇:渲染引擎与性能拆解——自绘vs原生渲染vs Bridge的终极对决 第3篇:架构哲学与工程化——从开发体验到CI/CD的全维度对比 第4篇:技术选型决策树——什么团队、什么项目该选什么框架(本篇 · 完结) 上个月,有三个不同的朋友分别找我聊跨平台选型。 第一个是创业


RAG 系列(二):用 LangChain 搭建你的第一个 RAG Pipeline
冬奇Lab2026/4/30

从 100 行代码到生产级 Pipeline 上一篇我们用手写 Python 搭了一个最小 RAG,100 行代码跑通了核心逻辑。但如果你想把那套代码搬到生产环境,很快就会撞上一堵墙。 要加载 PDF? 你需要 PyPDF2 或 pdfplumber,然后发现表格、页眉页脚的解析是一场噩梦。 要切分文本? 你那个朴素的 text.split("\n\n") 会把句子拦腰截断、破坏代码块,或者切出超长的块直接把 Token 上限撑爆。 想换个向量数据库? 祝你下午愉快——每个数据库的 API 都不


告别 jq 噩梦!这款 JSON 神器 fx 让你在终端体验“丝滑”的数据操作
GetcharZp2026/4/21

还在为复杂的 jq 语法抓狂?antonmedv/fx 带着交互式 TUI 和纯正 JavaScript 语法来了!JSON 调试、过滤、转换,一个工具全搞定。 在程序员的日常摸鱼……哦不,日常开发中,JSON 绝对是出现频率最高的朋友。 不管是调用后端接口、查看 K8s 配置,还是分析爬虫数据,面对满屏密密麻麻、甚至没有缩进的原始 JSON 字符串,我们的第一反应通常是: 打开浏览器,搜索“JSON 在线格式化”。 把数据粘进去,点一下“美化”。 忍受网页弹窗广告,或者担心敏感数据泄露。


实测对比:哪款开源 Kubernetes MySQL Operator 最值得用?(2026 深度评测)
小猿姐2026/4/13

本文基于作者在 AWS EKS 上对四款 MySQL Operator 的真实部署与测试,覆盖集群搭建、高可用切换、弹性扩缩容、动态参数、TLS 等维度,适合正在评估 MySQL Kubernetes 方案的工程师参考。 一、为什么要做这次对比测试? 过去两年,越来越多的团队开始将 MySQL 从虚拟机迁移到 Kubernetes。驱动力很直接:统一的基础设施管控、更快的弹性扩容、以及 GitOps 风格的声明式运维。 但随之而来的问题是:MySQL Operator 怎么选? 我们决定不依赖


Claude Code 的权限系统是如何工作的
candyTong2026/4/5

在 agent runtime 的实现里,权限从来不是外围配置项,而是执行系统的一部分。只要模型开始改文件、跑命令、调用外部工具,系统就必须回答一个核心问题:这一步是否允许执行,以及这个判断应该在执行链路的哪个位置完成。 Claude Code 的权限系统很适合作为分析样本。它不是在工具外面额外包一层确认框,而是把权限判断直接嵌入工具调用链路,让一次工具调用从发起到落地,始终伴随一套可组合、可回写的运行时裁决。 文章从一个常见工作场景切入,分析权限系统实际解决的问题、内部层次划分,以及这些设计对

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读