

思路一:序列DP
- 一个很直观的动态规划思路,遍历整个sequence找子串的个数;
- 定义dp[i]比较有趣,含义是以当前dp[i]为结尾的子串能与模板word构成多少重复值,因为从前向后遍历,可以发现:【sequence长度为n,word长度为m】
- 从m开始遍历,每次只比较最近一个m长的子串是否重复,然后更新数据;
- 更新数据在dp[i−m]的基础上增加,即最近的不重合的部分,因为要求两次重复相连;
- 过程中维护一个最大值即可。
Java
class Solution {
public int maxRepeating(String sequence, String word) {
int n = sequence.length(), m = word.length();
if (n < m)
return 0;
int res = 0;
int[] dp = new int[n + 10];
for (int i = m; i <= n; i++) {
String sub = sequence.substring(i - m, i);
if (sub.equals(word)) {
dp[i] = dp[i -m] + 1;
res = Math.max(res, dp[i]);
}
}
return res;
}
}
- 时间复杂度:O(m×n)
- 空间复杂度:O(n)
C++
class Solution {
public:
int maxRepeating(string sequence, string word) {
int n = sequence.size(), m = word.size();
if (n < m)
return 0;
int res = 0;
int dp[n + 10];
memset(dp, 0, sizeof(dp));
for (int i = m; i <= n; i++) {
string sub = sequence.substr(i - m, m);
if (sub == word) {
dp[i] = dp[i -m] + 1;
res = max(res, dp[i]);
}
}
return res;
}
};
- 时间复杂度:O(m×n)
- 空间复杂度:O(n)
思路二:字符串哈希
- 因为字符串的操作really昂贵且与长度有很大关系,数字操作相对好些,那么考虑用数字表示字符串——字符串哈希数组;
- 具体就是乘一个很大的数,用一个较大范围的数表示不同的段;
- 因为转换哈希的过程都是线性计算,所以通过加减即可得到对应小段的哈希表示;
- 数组h[i]存放字符串第i位字符的哈希值,是前续内容乘一个巨大P值然后加上当前字母的编码大小;
- 数组p[i]用来存放Pi便于计算,也可以不存每次都现场算,繁琐复杂一点;
- P要是一个质数,不然如果遇到某个字符是它的因数就不唯一了,大小要根据实际数据范围从小到大去尝试。
- 构建好哈希值之后,就和上面一样了,只不过这里比较的是无符号数值,比取子串再比较简单得多。
Java
class Solution {
public int maxRepeating(String sequence, String word) {
int n = sequence.length(), m = word.length();
if (n < m)
return 0;
String s = sequence + word;
int P = 1313131, N = s.length();
long[] h = new long[N + 10], p = new long[N + 10];
p[0] = 1;
for (int i = 1; i <= N; i++) {
h[i] = h[i - 1] * P + s.charAt(i - 1);
p[i] = p[i - 1] * P;
}
long hash = h[N] - h[N - m] * p[m];
int res = 0;
int[] dp = new int[n + 10];
for (int i = m; i <= n; i++) {
long cur = h[i] - h[i - m] * p[m];
if (cur == hash) {
dp[i] = dp[i -m] + 1;
res = Math.max(res, dp[i]);
}
}
return res;
}
}
- 时间复杂度:O(n+m)
- 空间复杂度:O(n+m)
C++
- 注意C++和java中数据类型的不同!
- 因为是用很大的数转换字符串,所以报了
overflow的错,然后改用了long long还是不行,去查了发现java里面都是无符号数,而C++默认有符号!那改成无符号数就ok啦~
class Solution {
public:
int maxRepeating(string sequence, string word) {
int n = sequence.size(), m = word.size();
if (n < m)
return 0;
string s = sequence + word;
int P = 1313131, N = s.size();
unsigned long h [N + 10], p[N + 10];
p[0] = 1;
for (int i = 1; i <= N; i++) {
h[i] = h[i - 1] * P + s[i - 1];
p[i] = p[i - 1] * P;
}
unsigned long hash = h[N] - h[N - m] * p[m];
int res = 0;
int dp[n + 10];
memset(dp, 0, sizeof(dp));
for (int i = m; i <= n; i++) {
unsigned long cur = h[i] - h[i - m] * p[m];
if (cur == hash) {
dp[i] = dp[i -m] + 1;
res = max(res, dp[i]);
}
}
return res;
}
};
- 时间复杂度:O(n+m)
- 空间复杂度:O(n+m)
思路三:KMP
- 复习一下前不久【不太久吧】学的KMP,重点在于根据模板找“备胎”数组nxt,即失败了下一个如何跳转;
- 然后基于这个就顺着找,和上面的思路类似:
- 但循环需要从0开始,上面是回去找子串,但KMP不回溯,比较的仅是当前字符,然后调整j指针【模板指针】的位置;【有点点绕】
Java
class Solution {
public int maxRepeating(String sequence, String word) {
int n = sequence.length(), m = word.length();
if (n < m)
return 0;
int[] nxt = new int[m];
Arrays.fill(nxt, -1);
for (int i = 1; i < m; i++) {
int j = nxt[i - 1];
while (j != -1 && word.charAt(j + 1) != word.charAt(i))
j = nxt[j];
if (word.charAt(j + 1) == word.charAt(i))
nxt[i] = j + 1;
}
int[] dp = new int[n + 10];
int j = -1;
for (int i = 0; i < n; i++) {
while (j != -1 && word.charAt(j + 1) != sequence.charAt(i))
j = nxt[j];
if (word.charAt(j + 1) == sequence.charAt(i)){
j++;
if (j == m - 1) {
dp[i] = (i >= m ? dp[i - m] : 0) + 1;
j = nxt[j];
}
}
}
return Arrays.stream(dp).max().getAsInt();
}
}
- 时间复杂度:O(n+m)
- 空间复杂度:O(n+m)
C++
class Solution {
public:
int maxRepeating(string sequence, string word) {
int n = sequence.size(), m = word.size();
if (n < m)
return 0;
int nxt[m];
memset(nxt, -1, sizeof(nxt));
for (int i = 1; i < m; i++) {
int j = nxt[i - 1];
while (j != -1 && word[j + 1] != word[i])
j = nxt[j];
if (word[j + 1] == word[i])
nxt[i] = j + 1;
}
int dp[n + 10];
memset(dp, 0, sizeof(dp));
int j = -1, res = 0;
for (int i = 0; i < n; i++) {
while (j != -1 && word[j + 1] != sequence[i])
j = nxt[j];
if (word[j + 1] == sequence[i]){
j++;
if (j == m - 1) {
dp[i] = (i >= m ? dp[i - m] : 0) + 1;
j = nxt[j];
res = max(res, dp[i]);
}
}
}
return res;
}
};
- 时间复杂度:O(n+m)
- 空间复杂度:O(n+m)
思路四:枚举+内置函数【简单题的真实面貌】
- 因为数据范围比较友好,所以可以直接暴力解决,那还不赶紧合理运用一些内置函数,暴力就一点脑子都不动!
Java
class Solution {
public int maxRepeating(String sequence, String word) {
int res = 0;
while (sequence.contains(word.repeat(res))) {
res++;
}
return res - 1;
}
}
C++
class Solution {
public:
int maxRepeating(string sequence, string word) {
int res = 0;
string cur = word;
while (sequence.find(cur) != -1) {
res++;
cur += word;
}
return res;
}
};
Rust
impl Solution {
pub fn max_repeating(sequence: String, word: String) -> i32 {
let mut res = 0;
while sequence.contains(&word.repeat(res as usize)) {
res += 1;
}
res - 1
}
}
总结
- 力扣改了新界面,调整了一些图标位置
- 对我有用:左右可伸缩幅度变大,控制台上下可调整大小、左右可换位置,题解查找更方便;
- 么的大用:修改题目翻页位置,增加计时器……
- 改了新的提交界面和题解界面不太习惯……
- 复习K了一下KMP算法,意料之外地学了一些公式写法,OI Wiki真的太棒了~
- Rust就随便写个内置函数版;
- 难以想象这竟然是简单题。