Hard
题目描述
你正在一个字符一个字符地构建长度为 n 的字符串 s,每次将新字符添加到字符串的开头。字符串从 1 到 n 编号,长度为 i 的字符串标记为 si。
例如,对于 s = “abaca”,s1 == “a”,s2 == “ca”,s3 == “aca”,等等。
si 的分数是 si 和 sn 之间最长公共前缀的长度(注意 s == sn)。
给定最终字符串 s,返回每个 si 的分数之和。
示例 1:
输入:s = "babab"
输出:9
解释:
对于 s1 == "b",最长公共前缀是 "b",分数为 1。
对于 s2 == "ab",没有公共前缀,分数为 0。
对于 s3 == "bab",最长公共前缀是 "bab",分数为 3。
对于 s4 == "abab",没有公共前缀,分数为 0。
对于 s5 == "babab",最长公共前缀是 "babab",分数为 5。
分数之和为 1 + 0 + 3 + 0 + 5 = 9,所以返回 9。
示例 2:
输入:s = "azbazbzaz"
输出:14
解释:
对于 s2 == "az",最长公共前缀是 "az",分数为 2。
对于 s6 == "azbzaz",最长公共前缀是 "azb",分数为 3。
对于 s9 == "azbazbzaz",最长公共前缀是 "azbazbzaz",分数为 9。
对于所有其他 si,分数为 0。
分数之和为 2 + 3 + 9 = 14,所以返回 14。
提示:
- 1 <= s.length <= 10^5
- s 由小写英文字母组成
解题思路
解题思路
这道题的关键在于理解题意:字符串 si 实际上是原字符串 s 的后缀。我们需要计算每个后缀与整个字符串 s 的最长公共前缀长度,然后求和。
方法一:朴素算法
对每个后缀,直接与原字符串逐字符比较,找出最长公共前缀。时间复杂度为 O(n²),对于大数据会超时。
方法二:Z算法(推荐)
Z算法是专门解决字符串匹配问题的经典算法。Z[i] 表示从位置 i 开始的子串与整个字符串的最长公共前缀长度。
Z算法的核心思想:
- 维护一个窗口 [l, r],表示当前找到的最右边的匹配区间
- 对于每个位置 i,利用已计算的信息来加速计算 Z[i]
- 如果 i 在窗口内,可以利用对称性质快速初始化 Z[i]
具体实现:
- 如果 i <= r,则 Z[i] 初始化为 min(Z[i-l], r-i+1)
- 然后尝试扩展匹配,更新窗口 [l, r]
- 累加所有 Z[i] 值即为答案
时间复杂度:O(n),因为每个字符最多被访问两次 空间复杂度:O(n),用于存储Z数组
代码实现
class Solution {
public:
long long sumScores(string s) {
int n = s.length();
vector<int> z(n);
z[0] = n;
int l = 0, r = 0;
for (int i = 1; i < n; i++) {
if (i <= r) {
z[i] = min(z[i - l], r - i + 1);
}
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) {
z[i]++;
}
if (i + z[i] - 1 > r) {
l = i;
r = i + z[i] - 1;
}
}
long long result = 0;
for (int i = 0; i < n; i++) {
result += z[i];
}
return result;
}
};
class Solution:
def sumScores(self, s: str) -> int:
n = len(s)
z = [0] * n
z[0] = n
l = r = 0
for i in range(1, n):
if i <= r:
z[i] = min(z[i - l], r - i + 1)
while i + z[i] < n and s[z[i]] == s[i + z[i]]:
z[i] += 1
if i + z[i] - 1 > r:
l = i
r = i + z[i] - 1
return sum(z)
public class Solution {
public long SumScores(string s) {
int n = s.Length;
int[] z = new int[n];
z[0] = n;
int l = 0, r = 0;
for (int i = 1; i < n; i++) {
if (i <= r) {
z[i] = Math.Min(z[i - l], r - i + 1);
}
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) {
z[i]++;
}
if (i + z[i] - 1 > r) {
l = i;
r = i + z[i] - 1;
}
}
long result = 0;
for (int i = 0; i < n; i++) {
result += z[i];
}
return result;
}
}
/**
* @param {string} s
* @return {number}
*/
var sumScores = function(s) {
const n = s.length;
const z = new Array(n);
z[0] = n;
let l = 0, r = 0;
for (let i = 1; i < n; i++) {
if (i <= r) {
z[i] = Math.min(z[i - l], r - i + 1);
} else {
z[i] = 0;
}
while (i + z[i] < n && s[z[i]]
复杂度分析
| 复杂度类型 | Z算法 | 朴素算法 |
|---|---|---|
| 时间复杂度 | O(n) | O(n²) |
| 空间复杂度 | O(n) | O(1) |
相关题目
- . Longest Happy Prefix (Hard)