一、题目描述
定义字符串 base 为一个 "abcdefghijklmnopqrstuvwxyz" 无限环绕的字符串,所以 base 看起来是这样的:
"...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd....".
给你一个字符串 s ,请你统计并返回 s 中有多少 不同非空子串 也在 base 中出现。
示例 1:
输入:s = "a"
输出:1
解释:字符串 s 的子字符串 "a" 在 base 中出现。
示例 2:
输入:s = "cac"
输出:2
解释:字符串 s 有两个子字符串 ("a", "c") 在 base 中出现。
示例 3:
输入:s = "zab"
输出:6
解释:字符串 s 有六个子字符串 ("z", "a", "b", "za", "ab", and "zab") 在 base 中出现。
提示:
1 <= s.length <= 10^5- s 由小写英文字母组成
二、解题思路
这个问题可以通过动态规划的方法来解决。我们可以使用一个长度为26的数组count来记录以每个字母结尾的最长连续子串的长度。这里连续子串是指按照字母表顺序连续或者环绕字母表连续的子串。
我们遍历字符串s,并检查当前字符和前一个字符是否连续。如果是连续的,我们就增加当前连续子串的长度;如果不是连续的,我们将当前连续子串的长度重置为1。每当我们找到一个连续子串时,我们更新对应字母的count数组中的值,以确保记录的是最长的连续子串长度。
最后,我们将count数组中的所有值相加,得到的就是s中不同非空子串在base中出现的次数。
三、具体代码
class Solution {
public int findSubstringInWraproundString(String s) {
int[] count = new int[26]; // 用于记录以每个字母结尾的最长连续子串的长度
int maxLength = 0; // 当前连续子串的长度
for (int i = 0; i < s.length(); i++) {
// 检查当前字符和前一个字符是否连续
if (i > 0 && (s.charAt(i) == s.charAt(i - 1) + 1 || s.charAt(i) == 'a' && s.charAt(i - 1) == 'z')) {
maxLength++;
} else {
maxLength = 1;
}
// 更新以当前字符结尾的最长连续子串的长度
int index = s.charAt(i) - 'a';
count[index] = Math.max(count[index], maxLength);
}
// 计算所有不同非空子串的数量
int sum = 0;
for (int length : count) {
sum += length;
}
return sum;
}
}
这段代码首先初始化一个长度为26的数组count,然后遍历字符串s,在每次迭代中检查当前字符是否与前一个字符连续,并更新count数组。最后,将count数组中的所有值相加得到结果。
四、时间复杂度和空间复杂度
1. 时间复杂度
- 初始化
count数组的时间复杂度是O(1),因为它的长度是固定的26。 - 遍历字符串
s的时间复杂度是O(n),其中n是字符串s的长度。 - 在遍历过程中,对于每个字符,我们只进行常数时间的操作(比较字符、更新长度、更新数组元素)。
- 最后,遍历
count数组求和的时间复杂度是O(1),因为它的长度是固定的26。
综上所述,总的时间复杂度是O(n),其中n是字符串s的长度。
2. 空间复杂度
count数组使用的空间是O(1),因为它的长度是固定的26。- 除了
count数组,我们没有使用额外的空间,所有变量使用的空间都是常数级别的。
因此,总的空间复杂度是O(1)。这里我们忽略了存储输入字符串s所需的空间,因为题目要求分析不包括输入数据本身占用的空间。
五、总结知识点
-
数组的声明与初始化:
- 使用
int[] count = new int[26];声明并初始化了一个长度为26的整数数组,用于存储以每个字母结尾的最长连续子串的长度。
- 使用
-
字符串操作:
- 使用
String s来处理输入字符串。 - 使用
charAt(int index)方法来获取字符串中指定位置的字符。
- 使用
-
字符与整数的转换:
- 通过减去字符’a’,将字符转换为对应的索引(0-25),即
s.charAt(i) - 'a'。
- 通过减去字符’a’,将字符转换为对应的索引(0-25),即
-
循环与条件判断:
- 使用
for循环遍历字符串s。 - 使用
if语句来检查当前字符和前一个字符是否连续。
- 使用
-
逻辑运算符:
- 使用
||(逻辑或)来检查两个条件中的任意一个是否成立。 - 使用
&&(逻辑与)来确保两个条件同时成立。
- 使用
-
数组元素的更新:
- 使用
Math.max()函数来更新数组count中的元素,确保存储的是最长连续子串的长度。
- 使用
-
累加操作:
- 使用一个
for循环遍历数组count,并将所有元素累加到变量sum中。
- 使用一个
-
方法定义与返回值:
- 定义了一个名为
findSubstringInWraproundString的公共方法,该方法接收一个字符串参数并返回一个整数结果。
- 定义了一个名为
-
算法思想:
- 使用动态规划的思想来解决问题,通过记录每个字母结尾的最长连续子串长度来避免重复计算。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

1304

被折叠的 条评论
为什么被折叠?



