leetcode
leetcode 401 ~ 450
环绕字符串中唯一的子字符串

环绕字符串中唯一的子字符串

难度:

标签:

题目描述

定义字符串 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 <= 105
  • s 由小写英文字母组成

代码结果

运行时间: 62 ms, 内存: 0.0 MB


/*
 * Problem statement:
 * Given a string `s`, find the number of unique non-empty substrings of `s` that also appear in an infinitely repeated sequence of the English alphabet, `base = "abcdefghijklmnopqrstuvwxyz"`.
 *
 * Approach using Java Streams:
 * 1. Use a HashSet to store unique valid substrings.
 * 2. Utilize IntStream to generate start and end indices for substrings.
 * 3. For each generated substring, verify its validity in the cyclic base string.
 * 4. Use the helper method `isInBase` to check validity and add to set if valid.
 * 5. Return the size of the set.
 */
 
import java.util.*;
import java.util.stream.IntStream;
 
public class SolutionStream {
    public int findSubstringInWraproundString(String s) {
        Set<String> uniqueSubstrings = new HashSet<>();
        int n = s.length();
 
        // Use IntStream to generate start and end indices
        IntStream.range(0, n).forEach(i ->
            IntStream.range(i + 1, n + 1).forEach(j -> {
                String sub = s.substring(i, j);
                if (isInBase(sub)) {
                    uniqueSubstrings.add(sub);
                }
            })
        );
 
        return uniqueSubstrings.size();
    }
 
    private boolean isInBase(String sub) {
        int len = sub.length();
        for (int i = 1; i < len; i++) {
            if ((sub.charAt(i) - sub.charAt(i - 1) + 26) % 26 != 1) {
                return false;
            }
        }
        return true;
    }
}

解释

方法:

这个题解的思路是使用一个长度为26的数组c来记录以每个字母结尾的最长连续子串的长度。遍历字符串s,如果当前字符和前一个字符的ASCII码差为1或-25(说明是相邻的字母),则继续维护当前的连续子串;否则,更新数组c中对应字母的最大长度,并重新开始一个新的连续子串。最后,从后往前遍历数组c,如果前一个字母的最长子串长度比当前字母的最长子串长度大1,则更新当前字母的最长子串长度。最终数组c中的所有元素之和即为所求的不同非空子串的数量。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
题解中提到的数组c用来记录以每个字母结尾的最长连续子串的长度,如何处理字符的循环性,即从'z'到'a'的过渡?
题解中使用数组c来记录以每个字母结尾的最长连续子串的长度。字符的循环性,即从'z'到'a'的过渡,是通过检查当前字符和前一个字符的ASCII码差值来处理的。如果差值为1,表示两个字符是顺序相邻的;如果差值为-25,表示这是从'z'到'a'的过渡。这种方式使得算法能够正确处理环绕(从'z'回到'a')的情况,符合题目中'环绕字符串'的要求。
🦆
题解中的算法在遇到当前字符和前一个字符不是相邻的情况下,为什么要更新连续子串的起始位置?这样的更新对结果的正确性有何影响?
当当前字符和前一个字符不是相邻的,这意味着当前的连续子串已经结束,不能再继续延伸。此时,需要更新连续子串的起始位置以开始一个新的子串计算。这样的更新确保了每个连续子串都被正确地记录,并且其长度只被计算一次。这对结果的正确性是必要的,因为它保证了每个以不同字母结尾的最长连续子串都能被正确地更新和记录。
🦆
在遍历数组c时,使用了从后往前的方法来更新数组元素,这种方法的目的是什么?它如何帮助我们获得正确的子串数量?
从后往前遍历数组c并更新元素的主要目的是确保每个字符的子串长度都是最大可能的。特别是对于环绕的场景,如从'z'回到'a',这种更新方法可以确保前一个字母的最长子串长度能正确影响到后一个字母。这种从后往前的更新方法能够确保所有可能的连续子串都被考虑到,从而帮助我们正确计算出不同非空子串的总数量。
🦆
题解中在处理最后一个连续子串时直接更新了数组c,如果最后一个字母在前面已经有了更长的记录,这种处理方式是否会导致结果出错?
在处理最后一个连续子串时直接更新数组c,这样做是为了确保此子串能被计入统计。算法在更新数组c时会检查当前记录是否小于新计算出的连续子串长度,只有当新的长度更大时才进行更新。这样的处理方式不会导致结果出错,因为它遵循了始终记录最长连续子串长度的原则,确保了每个以不同字母结尾的子串长度都是最大可能的。

相关问题