leetcode
leetcode 2701 ~ 2750
到目标字符串的最短距离

到目标字符串的最短距离

难度:

标签:

题目描述

You are given a 0-indexed circular string array words and a string target. A circular array means that the array's end connects to the array's beginning.

  • Formally, the next element of words[i] is words[(i + 1) % n] and the previous element of words[i] is words[(i - 1 + n) % n], where n is the length of words.

Starting from startIndex, you can move to either the next word or the previous word with 1 step at a time.

Return the shortest distance needed to reach the string target. If the string target does not exist in words, return -1.

 

Example 1:

Input: words = ["hello","i","am","leetcode","hello"], target = "hello", startIndex = 1
Output: 1
Explanation: We start from index 1 and can reach "hello" by
- moving 3 units to the right to reach index 4.
- moving 2 units to the left to reach index 4.
- moving 4 units to the right to reach index 0.
- moving 1 unit to the left to reach index 0.
The shortest distance to reach "hello" is 1.

Example 2:

Input: words = ["a","b","leetcode"], target = "leetcode", startIndex = 0
Output: 1
Explanation: We start from index 0 and can reach "leetcode" by
- moving 2 units to the right to reach index 3.
- moving 1 unit to the left to reach index 3.
The shortest distance to reach "leetcode" is 1.

Example 3:

Input: words = ["i","eat","leetcode"], target = "ate", startIndex = 0
Output: -1
Explanation: Since "ate" does not exist in words, we return -1.

 

Constraints:

  • 1 <= words.length <= 100
  • 1 <= words[i].length <= 100
  • words[i] and target consist of only lowercase English letters.
  • 0 <= startIndex < words.length

代码结果

运行时间: 19 ms, 内存: 16.2 MB


/*
 * 思路:
 * 使用Java Stream来简化代码。
 * 我们可以使用IntStream来遍历索引,并计算顺时针和逆时针的距离。
 * 使用filter找到目标字符串的位置,然后使用mapToInt计算距离。
 * 最后使用min找到最小距离。
 */

import java.util.stream.IntStream;

public class Solution {
    public int closetTarget(String[] words, String target, int startIndex) {
        int n = words.length;
        return IntStream.range(0, n)
                .filter(i -> words[i].equals(target))
                .map(i -> {
                    int distanceClockwise = (i - startIndex + n) % n;
                    int distanceCounterClockwise = (startIndex - i + n) % n;
                    return Math.min(distanceClockwise, distanceCounterClockwise);
                })
                .min()
                .orElse(-1);
    }
}

解释

方法:

题解首先初始化两个计数器x和y分别表示向右和向左查找目标字符串target的距离。从startIndex开始,首先向右循环遍历words数组,每次移动后增加x的计数。如果索引s超过数组长度,则将其重置为0,模拟环形数组的行为。如果向右的遍历次数x超过数组长度而还未找到目标,则返回-1,表示target不在数组中。完成向右的遍历后,同样的方法向左遍历,每次递减索引s,并增加计数器y。向左遍历时如果索引s变为负数,则重置为数组的最后一个元素,再次模拟环形数组。最后,函数返回x和y中的较小值,即为到达target的最短距离。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
题解中提到如果向右遍历的次数超过数组长度还未找到`target`就返回-1,但是这是否考虑了环形数组的特性,即可能需要多轮遍历才能找到目标?
题解中的方法确实考虑了环形数组的特性,通过将索引重置为0来模拟环形行为。然而,设置如果遍历次数超过数组长度就返回-1的逻辑,其实是基于一个假设:如果在整个数组中都没有找到`target`,那么即使继续环绕搜索也是无效的。这是因为一旦遍历的次数等于数组长度,意味着已经检查过数组中的每一个元素。如果在这个过程中都没有找到目标,那么继续遍历也不会找到。所以,这不是忽视了环形数组的特性,而是一种避免无效搜索的优化措施。
🦆
题解中向左搜索时,将索引s调整为数组末尾而不是采用模运算,这种方式是否有特殊的考虑?为什么不使用`(s - 1 + n) % n`的方式来处理?
将索引s直接调整到数组末尾,而不使用`(s - 1 + n) % n`的方式,本质上是两种不同的实现方式,但都能正确处理索引环绕的情况。直接设置`s = len(words) - 1`在这种情况下简化了计算,避免了每次都进行取模运算,可能在某些情况下提高了效率。不过,使用`(s - 1 + n) % n`的方式具有更好的通用性和可读性,对于维护和理解代码是有帮助的。选择哪种方式,取决于程序员对效率和代码可维护性的考量。
🦆
在题解中使用了两个独立的while循环来分别计算向右和向左的距离,这种实现方式是否会导致重复计算?能否通过一次遍历完成两个方向的搜索?
使用两个独立的while循环确实有可能导致对某些元素的重复计算,尤其是如果`target`接近`startIndex`时。通过一次遍历同时计算两个方向的距离是可能的,可以通过同时维护两个方向的索引和计数器来实现。这样的实现可以减少遍历次数,提高效率。例如,可以从`startIndex`出发,同时向左和向右扩展,每步更新两个方向的索引和距离计数器,直到找到`target`。这样的方法能够减少不必要的重复计算,尤其是在`target`位置距离`startIndex`较近时更为高效。

相关问题