leetcode
leetcode 2701 ~ 2750
找出数组中的美丽下标 II

找出数组中的美丽下标 II

难度:

标签:

题目描述

You are given a 0-indexed string s, a string a, a string b, and an integer k.

An index i is beautiful if:

  • 0 <= i <= s.length - a.length
  • s[i..(i + a.length - 1)] == a
  • There exists an index j such that:
    • 0 <= j <= s.length - b.length
    • s[j..(j + b.length - 1)] == b
    • |j - i| <= k

Return the array that contains beautiful indices in sorted order from smallest to largest.

 

Example 1:

Input: s = "isawsquirrelnearmysquirrelhouseohmy", a = "my", b = "squirrel", k = 15
Output: [16,33]
Explanation: There are 2 beautiful indices: [16,33].
- The index 16 is beautiful as s[16..17] == "my" and there exists an index 4 with s[4..11] == "squirrel" and |16 - 4| <= 15.
- The index 33 is beautiful as s[33..34] == "my" and there exists an index 18 with s[18..25] == "squirrel" and |33 - 18| <= 15.
Thus we return [16,33] as the result.

Example 2:

Input: s = "abcd", a = "a", b = "a", k = 4
Output: [0]
Explanation: There is 1 beautiful index: [0].
- The index 0 is beautiful as s[0..0] == "a" and there exists an index 0 with s[0..0] == "a" and |0 - 0| <= 4.
Thus we return [0] as the result.

 

Constraints:

  • 1 <= k <= s.length <= 5 * 105
  • 1 <= a.length, b.length <= 5 * 105
  • s, a, and b contain only lowercase English letters.

代码结果

运行时间: 344 ms, 内存: 52.3 MB


/*
题目思路:
1. 使用 IntStream 生成所有可能的 i 和 j。
2. 过滤出符合 a 和 b 的子字符串。
3. 检查 |j - i| 是否小于等于 k。
4. 将结果收集并排序。
*/

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class BeautifulIndicesStream {
    public List<Integer> findBeautifulIndices(String s, String a, String b, int k) {
        int sLength = s.length();
        int aLength = a.length();
        int bLength = b.length();

        return IntStream.range(0, sLength - aLength + 1)
                .filter(i -> s.substring(i, i + aLength).equals(a))
                .filter(i -> IntStream.range(0, sLength - bLength + 1)
                        .anyMatch(j -> s.substring(j, j + bLength).equals(b) && Math.abs(i - j) <= k))
                .boxed()
                .sorted()
                .collect(Collectors.toList());
    }

    public static void main(String[] args) {
        BeautifulIndicesStream bis = new BeautifulIndicesStream();
        String s = "isawsquirrelnearmysquirrelhouseohmy";
        String a = "my";
        String b = "squirrel";
        int k = 15;
        System.out.println(bis.findBeautifulIndices(s, a, b, k)); // 输出: [16, 33]
    }
}

解释

方法:

本题解的主要思路是找出所有满足特定条件的下标对。首先,使用辅助函数g来找到字符串a或b中最小的重复模式长度,这有助于后续的优化。函数f0是用来找出字符串s中所有包含字符串a或b的起始下标。函数f用于优化搜索过程,如果字符串a或b有重复的模式,f会尽量减少搜索次数,从而只搜索可能的开始下标。最后,根据这些起始下标,检查它们之间的距离是否满足条件k。如果a和b是同一个字符串,可以进一步减少计算量。最终,将满足条件的下标存入结果数组并返回。

时间复杂度:

O(n * m)

空间复杂度:

O(n)

代码细节讲解

🦆
在实现函数`g`时,如何确保找到的是最小的重复模式长度,这里的递归逻辑是否可能导致重复检查某些长度?
函数`g`通过递归查找字符串中的最小重复模式长度。它首先尝试在字符串`a`中找到从某个位置`i`开始的子字符串`a[:i]`的下一个出现,如果找到了,会检查这个位置是否能够让整个字符串被这个长度整除且模式正确。如果不符合,`g`会递归地尝试下一个长度。虽然这种递归逻辑可能看起来会检查某些长度多次,但实际上每次递归调用时考虑的起始位置`i`都在增加,这意味着它不会重复检查相同的长度,而是逐步增加探索的长度,确保了寻找最小重复模式的高效性。
🦆
函数`f0`中,当找到一个匹配的索引后,为什么要将索引`i`加1再继续搜索,这样会不会漏掉某些重叠的匹配情况?
函数`f0`在找到字符串`a`在字符串`s`中的一个匹配后,索引`i`加1再继续搜索是为了找出所有可能的匹配位置,包括那些重叠的匹配。如果我们不将`i`增加,那么搜索就会在找到的第一个匹配处停止,无法继续向后查找。虽然这样做确实能找到所有匹配,包括重叠的,但它也增加了搜索的时间复杂度,因为每次找到匹配后只前进一个字符,会进行大量的重复检查。在某些情况下,可以考虑优化这个过程,比如在找到匹配后跳过整个匹配长度,但这需要根据具体情况调整策略。
🦆
对于函数`f`,如何决定使用优化搜索而不是简单的全搜索,特别是当字符串`a`和`b`很短时,这个优化是否仍然有效?
函数`f`通过检测字符串中的重复模式来决定是否使用优化搜索。当字符串较短时,这种优化可能不会带来显著的性能提升,因为重复模式可能不存在或者模式长度接近整个字符串长度,这使得优化的效果不明显。在这种情况下,简单的全搜索(即逐一检查每个可能的位置)可能更为直接和高效。然而,如果字符串较长且存在明显的重复模式,优化搜索可以显著减少搜索次数,通过跳过一些不可能的匹配位置来提高效率。因此,是否使用优化搜索应根据字符串的实际情况和特性来决定,特别是在字符串很短的情况下可能不需要复杂的优化。

相关问题