LeetCode题练习与总结:环绕字符串中唯一的子字符串--467

一、题目描述

定义字符串 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'
  • 循环与条件判断

    • 使用for循环遍历字符串s
    • 使用if语句来检查当前字符和前一个字符是否连续。
  • 逻辑运算符

    • 使用||(逻辑或)来检查两个条件中的任意一个是否成立。
    • 使用&&(逻辑与)来确保两个条件同时成立。
  • 数组元素的更新

    • 使用Math.max()函数来更新数组count中的元素,确保存储的是最长连续子串的长度。
  • 累加操作

    • 使用一个for循环遍历数组count,并将所有元素累加到变量sum中。
  • 方法定义与返回值

    • 定义了一个名为findSubstringInWraproundString的公共方法,该方法接收一个字符串参数并返回一个整数结果。
  • 算法思想

    • 使用动态规划的思想来解决问题,通过记录每个字母结尾的最长连续子串长度来避免重复计算。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直学习永不止步

谢谢您的鼓励,我会再接再厉的!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值