leetcode
leetcode 2301 ~ 2350
找到最长的半重复子字符串

找到最长的半重复子字符串

难度:

标签:

题目描述

You are given a 0-indexed string s that consists of digits from 0 to 9.

A string t is called a semi-repetitive if there is at most one consecutive pair of the same digits inside t. For example, 0010, 002020, 0123, 2002, and 54944 are semi-repetitive while 00101022, and 1101234883 are not.

Return the length of the longest semi-repetitive substring inside s.

A substring is a contiguous non-empty sequence of characters within a string.

 

Example 1:

Input: s = "52233"
Output: 4
Explanation: The longest semi-repetitive substring is "5223", which starts at i = 0 and ends at j = 3. 

Example 2:

Input: s = "5494"
Output: 4
Explanation: s is a semi-reptitive string, so the answer is 4.

Example 3:

Input: s = "1111111"
Output: 2
Explanation: The longest semi-repetitive substring is "11", which starts at i = 0 and ends at j = 1.

 

Constraints:

  • 1 <= s.length <= 50
  • '0' <= s[i] <= '9'

代码结果

运行时间: 44 ms, 内存: 16.1 MB


/*
题目思路:
使用Java Stream的功能来解决这个问题。我们仍然可以使用滑动窗口的方法,但这次用Stream API来处理字符串和计算长度。
*/

import java.util.stream.IntStream;

public class Solution {
    public int longestSemiRepetitiveSubstring(String s) {
        return IntStream.range(0, s.length())
                .flatMap(i -> IntStream.range(i + 1, s.length() + 1)
                        .map(j -> s.substring(i, j)))
                .filter(sub -> isSemiRepetitive(sub))
                .map(String::length)
                .max()
                .orElse(0);
    }

    private boolean isSemiRepetitive(String s) {
        int count = 0;
        for (int i = 1; i < s.length(); i++) {
            if (s.charAt(i) == s.charAt(i - 1)) {
                count++;
            }
            if (count > 1) {
                return false;
            }
        }
        return true;
    }
}

解释

方法:

题解采用了双指针(滑动窗口)的方法来找到最长的半重复子字符串。这里使用了两个指针left和right分别表示当前考虑的子字符串的开始和结束位置。变量same用于记录当前窗口内相邻字符相等的对数,pre用于记录最后一对相等字符的位置。遍历字符串s,当发现相邻字符相等时,same增加。如果same等于2(即存在两对相等的相邻字符),则计算当前的子字符串长度并尝试更新最大长度ans。然后调整left到上一对相等字符之后的位置,重新计算窗口。遍历结束后,需要比较一次以确保最后的窗口也被考虑进最大长度。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在实现双指针算法时,如何确保在移动左指针left后,窗口内的状态(例如same计数)依然正确无误?
在这个算法中,当左指针left移动到前一对相等字符之后的位置时,我们减少了same计数,因为我们排除了一个已经计数过的相等对。这样做是为了保持same计数的准确性,反映当前窗口内相邻字符相等的对数。通过这种方式,我们确保在移动左指针后,窗口内的状态(same计数)仍然正确,反映当前考虑的子字符串的实际情况。
🦆
为什么在遇到第二对相邻的相等字符时,选择将左指针移动到上一对相等字符之后的位置,而不是其他可能的位置?
选择将左指针移动到上一对相等字符之后的位置是为了尽可能保持窗口的大小,同时确保窗口内不超过一对相等字符。这样做可以使窗口在满足半重复条件(即至多包含一对相邻相等字符)的前提下,尽可能大,从而有可能获得更长的子字符串长度。如果选择其他位置,可能会不必要地减小窗口大小,从而错过更优的解。
🦆
在算法的结束时,为什么需要再次使用max函数比较right - left和ans,这一步是解决什么问题?
在算法结束时需要再次使用max函数比较right - left和ans,是因为最后一个考虑的窗口可能是最长的有效窗口,但未在循环中更新ans。特别是如果字符串的结尾部分符合条件但未因再次遇到相等对而更新,这一步就确保这最后的窗口长度也被考虑进最大长度的计算中。
🦆
如果字符串s中没有相邻的相等字符,算法的行为会是怎样?是否有必要特别处理这种情况?
如果字符串s中没有相邻的相等字符,same计数将始终为0。在这种情况下,算法将简单地遍历整个字符串,因为没有需要调整left指针的情况发生。最终,right将移动到字符串的末尾,ans通过最后一次比较可以得到整个字符串的长度,这反映了最长的半重复子字符串就是整个字符串本身。因此,没有必要特别处理这种情况,算法已经能够正确处理并给出正确答案。

相关问题