Skip to content

Commit 81b710c

Browse files
committed
✨feat: add 1668
1 parent b7d4d81 commit 81b710c

File tree

3 files changed

+198
-0
lines changed

3 files changed

+198
-0
lines changed

Index/字符串哈希.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
| [472. 连接词](https://leetcode-cn.com/problems/concatenated-words/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/concatenated-words/solution/gong-shui-san-xie-xu-lie-dpzi-fu-chuan-h-p7no/) | 困难 | 🤩🤩🤩🤩 |
55
| [686. 重复叠加字符串匹配](https://leetcode-cn.com/problems/repeated-string-match/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/repeated-string-match/solution/gong-shui-san-xie-yi-ti-san-jie-qia-chan-3hbr/) | 中等 | 🤩🤩🤩🤩 |
66
| [1044. 最长重复子串](https://leetcode-cn.com/problems/longest-duplicate-substring/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/longest-duplicate-substring/solution/gong-shui-san-xie-zi-fu-chuan-ha-xi-ying-hae9/) | 困难 | 🤩🤩🤩🤩 |
7+
| [1668. 最大重复子字符串](https://leetcode.cn/problems/maximum-repeating-substring/) | [LeetCode 题解链接](https://leetcode.cn/problems/maximum-repeating-substring/solution/by-ac_oier-xjhn/) | 简单 | 🤩🤩🤩🤩🤩 |
78
| [面试题 01.09. 字符串轮转](https://leetcode.cn/problems/string-rotation-lcci/) | [LeetCode 题解链接](https://leetcode.cn/problems/string-rotation-lcci/solution/by-ac_oier-2wo1/) | 中等 | 🤩🤩🤩🤩🤩 |
89

Index/序列 DP.md

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
| [1235. 规划兼职工作](https://leetcode.cn/problems/maximum-profit-in-job-scheduling/) | [LeetCode 题解链接](https://leetcode.cn/problems/maximum-profit-in-job-scheduling/solution/by-ac_oier-rgup/) | 困难 | 🤩🤩🤩🤩 |
2626
| [1473. 粉刷房子 III](https://leetcode-cn.com/problems/paint-house-iii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/paint-house-iii/solution/gong-shui-san-xie-san-wei-dong-tai-gui-h-ud7m/) | 困难 | 🤩🤩🤩🤩 |
2727
| [1537. 最大得分](https://leetcode.cn/problems/get-the-maximum-score/) | [LeetCode 题解链接](https://leetcode.cn/problems/get-the-maximum-score/solution/by-ac_oier-ht78/) | 困难 | 🤩🤩🤩🤩 |
28+
| [1668. 最大重复子字符串](https://leetcode.cn/problems/maximum-repeating-substring/) | [LeetCode 题解链接](https://leetcode.cn/problems/maximum-repeating-substring/solution/by-ac_oier-xjhn/) | 简单 | 🤩🤩🤩🤩🤩 |
2829
| [1713. 得到子序列的最少操作次数](https://leetcode-cn.com/problems/minimum-operations-to-make-a-subsequence/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/minimum-operations-to-make-a-subsequence/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-oj7yu/) | 困难 | 🤩🤩🤩🤩🤩 |
2930
| [1751. 最多可以参加的会议数目 II](https://leetcode-cn.com/problems/maximum-number-of-events-that-can-be-attended-ii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/maximum-number-of-events-that-can-be-attended-ii/solution/po-su-dp-er-fen-dp-jie-fa-by-ac_oier-88du/) | 困难 | 🤩🤩🤩🤩 |
3031

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[1668. 最大重复子字符串](https://leetcode.cn/problems/maximum-repeating-substring/solution/by-ac_oier-xjhn/)** ,难度为 **简单**
4+
5+
Tag : 「动态规划」、「序列 DP」、「字符串哈希」
6+
7+
8+
9+
给你一个字符串 `sequence`,如果字符串 `word` 连续重复 `k` 次形成的字符串是 `sequence` 的一个子字符串,那么单词 `word` 的 重复值为 `k`
10+
11+
单词 `word` 的 最大重复值 是单词 `word` 在 `sequence` 中最大的重复值。如果 `word` 不是 `sequence` 的子串,那么重复值 `k` 为 `0`
12+
13+
给你一个字符串 `sequence` 和 `word` ,请你返回 最大重复值 `k`
14+
15+
示例 1:
16+
```
17+
输入:sequence = "ababc", word = "ab"
18+
19+
输出:2
20+
21+
解释:"abab" 是 "ababc" 的子字符串。
22+
```
23+
示例 2:
24+
```
25+
输入:sequence = "ababc", word = "ba"
26+
27+
输出:1
28+
29+
解释:"ba" 是 "ababc" 的子字符串,但 "baba" 不是 "ababc" 的子字符串。
30+
```
31+
示例 3:
32+
```
33+
输入:sequence = "ababc", word = "ac"
34+
35+
输出:0
36+
37+
解释:"ac" 不是 "ababc" 的子字符串。
38+
```
39+
40+
提示:
41+
* $1 <= sequence.length <= 100$
42+
* $1 <= word.length <= 100$
43+
* `sequence` 和 `word` 都只包含小写英文字母。
44+
45+
---
46+
47+
### 序列 DP
48+
49+
为了方便,我们记 `sequence``ss`,记 `word``pp`,将两者长度分别记为 `n``m`
50+
51+
同时我们调整「字符串」以及「将要用到的动规数组」的下标从 $1$ 开始。
52+
53+
这是一道入门级的「序列 DP」运用题,容易想到 **定义 $f[i]$ 为了考虑以 `ss[i]` 结尾时的最大重复值**
54+
55+
不失一般性考虑 $f[i]$ 该如何转移:由于 `pp` 的长度已知,每次转移 $f[i]$ 时我们可以从 `ss` 中截取 **以 $ss[i]$ 为结尾,长度为 $m$ 的后缀字符串** `sub` 并与 `pp` 匹配,若两者相等,说明 `sub` 贡献了大小为 $1$ 的重复度,同时该重复度可累加在 $f[i - m]$ 上(好好回想我们的状态定义),即有状态转移方程:$f[i] = f[i - m] + 1$。
56+
57+
最终所有 $f[i]$ 的最大值即为答案。
58+
59+
Java 代码:
60+
```Java
61+
class Solution {
62+
public int maxRepeating(String ss, String pp) {
63+
int n = ss.length(), m = pp.length(), ans = 0;
64+
int[] f = new int[n + 10];
65+
for (int i = 1; i <= n; i++) {
66+
if (i - m < 0) continue;
67+
if (ss.substring(i - m, i).equals(pp)) f[i] = f[i - m] + 1;
68+
ans = Math.max(ans, f[i]);
69+
}
70+
return ans;
71+
}
72+
}
73+
```
74+
TypeScript 代码:
75+
```TypeScript
76+
function maxRepeating(ss: string, pp: string): number {
77+
let n = ss.length, m = pp.length, ans = 0
78+
const f = new Array<number>(n + 10).fill(0)
79+
for (let i = 1; i <= n; i++) {
80+
if (i - m < 0) continue
81+
if (ss.substr(i - m, i) == pp) f[i] = f[i - m] + 1
82+
ans = Math.max(ans, f[i])
83+
}
84+
return ans
85+
}
86+
```
87+
Python 代码:
88+
```Python
89+
class Solution:
90+
def maxRepeating(self, ss: str, pp: str) -> int:
91+
n, m, ans = len(ss), len(pp), 0
92+
f = [0] * (n + 10)
93+
for i in range(1, n + 1):
94+
if i - m < 0:
95+
continue
96+
if ss[i - m:i] == pp:
97+
f[i] = f[i - m] + 1
98+
ans = max(ans, f[i])
99+
return ans
100+
```
101+
* 时间复杂度:$O(n \times m)$
102+
* 空间复杂度:$O(n)$
103+
104+
---
105+
106+
### 字符串哈希
107+
108+
解法一的转移瓶颈在于:每次需要花费 $O(m)$ 的复杂度来生成子串,并进行字符串比较。
109+
110+
该过程可用「字符串哈希」进行优化:将 `ss``pp` 进行拼接得到完整字符串,并计算完整字符串的哈希数组和次方数组。随后从前往后检查 `ss`,若「某个以 $ss[i]$ 结尾长度为 `m` 的后缀字符串哈希值」与「 `pp` 字符串的哈希值」相等,说明找到了前驱状态值 $f[i - m]$,可进行转移。
111+
112+
我们通过 $O(n + m)$ 复杂度预处理了字符串哈希,将转移过程中「复杂度为 $O(m)$ 的子串截取与字符串比较」替换成了「复杂度为 $O(1)$ 的数值对比」,整体复杂度从 $O(n \times m)$ 下降到 $O(n + m)$。
113+
114+
> **不了解「字符串哈希」的同学可见前置 🧀 : [字符串哈希入门](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247489813&idx=1&sn=7f3bc18ca390d85b17655f7164d8e660)。里面详解字符串哈希基本原理以及哈希冲突简单处理方式**
115+
116+
Java 代码:
117+
```Java
118+
class Solution {
119+
public int maxRepeating(String ss, String pp) {
120+
int n = ss.length(), m = pp.length(), ans = 0;
121+
int[] f = new int[n + 10];
122+
123+
String s = ss + pp;
124+
int P = 1313131, N = s.length();
125+
long[] h = new long[N + 10], p = new long[N + 10];
126+
p[0] = 1;
127+
for (int i = 1; i <= N; i++) {
128+
h[i] = h[i - 1] * P + s.charAt(i - 1);
129+
p[i] = p[i - 1] * P;
130+
}
131+
long phash = h[N] - h[N - m] * p[m];
132+
133+
for (int i = 1; i <= n; i++) {
134+
if (i - m < 0) continue;
135+
long cur = h[i] - h[i - m] * p[m];
136+
if (cur == phash) f[i] = f[i - m] + 1;
137+
ans = Math.max(ans, f[i]);
138+
}
139+
return ans;
140+
}
141+
}
142+
```
143+
Python 代码:
144+
```Python
145+
class Solution:
146+
def maxRepeating(self, ss: str, pp: str) -> int:
147+
n, m, ans = len(ss), len(pp), 0
148+
f = [0] * (n + 10)
149+
150+
s = ss + pp
151+
P, N, MOD = 131, len(s), 987654321
152+
h, p = [0] * (N + 10), [0] * (N + 10)
153+
p[0] = 1
154+
for i in range(1, N + 1):
155+
h[i] = (h[i - 1] * P + ord(s[i - 1])) % MOD
156+
p[i] = (p[i - 1] * P) % MOD
157+
phash = (h[N] - h[N - m] * p[m]) % MOD
158+
159+
for i in range(1, n + 1):
160+
if i - m < 0:
161+
continue
162+
cur = (h[i] - h[i - m] * p[m]) % MOD
163+
if cur == phash:
164+
f[i] = f[i - m] + 1
165+
ans = max(ans, f[i])
166+
return ans
167+
```
168+
* 时间复杂度:$O(n + m)$
169+
* 空间复杂度:$O(n + m)$
170+
171+
---
172+
173+
### 总结
174+
175+
这里简单说下「线性 DP」和「序列 DP」的区别。
176+
177+
线性 DP 通常强调「状态转移所依赖的前驱状态」是由给定数组所提供的,即拓扑序是由原数组直接给出。更大白话来说就是通常有 $f[i][...]$ 依赖于 $f[i - 1][...]$。
178+
179+
这就限定了线性 DP 的复杂度是简单由「状态数量(或者说是维度数)」所决定。
180+
181+
序列 DP 通常需要结合题意来寻找前驱状态,即需要自身寻找拓扑序关系(例如本题,需要自己结合题意来找到可转移的前驱状态 $f[i - m]$)。
182+
183+
这就限定了序列 DP 的复杂度是由「状态数 + 找前驱」的复杂度所共同决定。也直接导致了序列 DP 有很多玩法,往往能够结合其他知识点出题,来优化找前驱这一操作,通常是利用某些性质,或是利用数据结构进行优化。
184+
185+
---
186+
187+
### 最后
188+
189+
这是我们「刷穿 LeetCode」系列文章的第 `No.1668` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
190+
191+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
192+
193+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode
194+
195+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
196+

0 commit comments

Comments
 (0)