leetcode
leetcode 2351 ~ 2400
最长合法子字符串的长度

最长合法子字符串的长度

难度:

标签:

题目描述

You are given a string word and an array of strings forbidden.

A string is called valid if none of its substrings are present in forbidden.

Return the length of the longest valid substring of the string word.

A substring is a contiguous sequence of characters in a string, possibly empty.

 

Example 1:

Input: word = "cbaaaabc", forbidden = ["aaa","cb"]
Output: 4
Explanation: There are 11 valid substrings in word: "c", "b", "a", "ba", "aa", "bc", "baa", "aab", "ab", "abc" and "aabc". The length of the longest valid substring is 4. 
It can be shown that all other substrings contain either "aaa" or "cb" as a substring. 

Example 2:

Input: word = "leetcode", forbidden = ["de","le","e"]
Output: 4
Explanation: There are 11 valid substrings in word: "l", "t", "c", "o", "d", "tc", "co", "od", "tco", "cod", and "tcod". The length of the longest valid substring is 4.
It can be shown that all other substrings contain either "de", "le", or "e" as a substring. 

 

Constraints:

  • 1 <= word.length <= 105
  • word consists only of lowercase English letters.
  • 1 <= forbidden.length <= 105
  • 1 <= forbidden[i].length <= 10
  • forbidden[i] consists only of lowercase English letters.

代码结果

运行时间: 545 ms, 内存: 32.1 MB


/*
 * 思路:
 * 1. 使用双指针(滑动窗口)方法来寻找最长的合法子字符串。
 * 2. 使用 Java Stream 处理字符串。
 * 3. 使用一个哈希集来存储所有 forbidden 子字符串。
 * 4. 当窗口内的子字符串包含 forbidden 中的任何字符串时,移动 start 指针以缩小窗口。
 * 5. 记录合法子字符串的最大长度。
 */
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class Solution {
    public int longestValidSubstring(String word, String[] forbidden) {
        Set<String> forbiddenSet = new HashSet<>(Arrays.asList(forbidden));

        int maxLength = 0;
        int start = 0;
        for (int end = 0; end < word.length(); end++) {
            for (int len = 1; len <= 10 && end - len + 1 >= start; len++) {
                if (forbiddenSet.contains(word.substring(end - len + 1, end + 1))) {
                    start = end - len + 2;
                    break;
                }
            }
            maxLength = Math.max(maxLength, end - start + 1);
        }

        return maxLength;
    }
}

解释

方法:

该题解采用了一种从右至左遍历单词的方法,同时使用了一个集合来存储禁止的单词,以及一个列表来存储所有禁止单词的长度。在每个左端点l从右向左移动的过程中,尝试检查从当前位置l开始的各种长度的子字符串是否在禁止集合中。如果找到了匹配的禁止子字符串,就更新右端点r为当前子字符串的结束位置前一个位置。这样可以保证从位置l到r的子字符串是合法的,从而更新最长合法子字符串的长度。这种方法有效地避免了对每个可能的子字符串重复检查,而是通过动态调整右端点来快速排除非法的子字符串。

时间复杂度:

O(n * maxLen)

空间复杂度:

O(f + l)

代码细节讲解

🦆
在算法中,为什么选择从右至左而不是从左至右遍历字符串`word`?
从右至左遍历字符串允许算法在发现禁止子字符串时即时调整右边界。这种方法可以从当前位置向左扩展合法子字符串的长度,一旦找到禁止的子字符串,就可以立即更新右边界,避免进一步的无效检查,并快速确定从当前左端点到新的右边界的合法子字符串长度。如果从左至右遍历,每次遇到禁止子字符串都需要重新评估右边界,这可能会增加复杂性和计算量。
🦆
算法中使用了一个列表来存储所有禁止单词的长度并进行排序,排序这一步的目的是什么?
排序禁止单词的长度可以使得在检查子字符串时更加高效。从最短到最长的顺序检查可以在发现禁止子字符串的最早时刻终止进一步的检查,减少不必要的比较。此外,这种排序还有助于在遍历过程中更快地决定何时停止检查更长的子字符串,尤其是当当前起点加上禁止单词的长度超过了当前的右边界时。
🦆
请解释在遍历过程中如何使用变量`r`来动态调整合法子字符串的右边界?
在算法执行过程中,变量`r`用于表示当前识别的合法子字符串的最远右边界。当从右至左遍历时,如果在位置`l`发现一个禁止的子字符串,算法会将`r`更新为该禁止子字符串结束位置的前一个位置(即`l + length - 1`),这样可以确保从位置`l`到`r`的字符串是合法的。每次更新`r`后,算法会根据当前`l`和新的`r`重新计算并更新最长合法子字符串的长度。
🦆
在某些情况下,可能存在多个禁止单词具有相同的长度,算法是如何处理这种情况的?
算法通过将所有禁止单词存储在一个集合中来处理这种情况。这意味着不管禁止单词重复多少次或有多少个禁止单词具有相同的长度,集合中每个独特的子字符串都只存储一次。在遍历过程中,算法检查从当前位置开始的特定长度的子字符串是否存在于禁止集合中。因此,即便多个禁止单词有相同的长度,算法都能有效地识别并处理这些禁止的子字符串。

相关问题