leetcode
leetcode 901 ~ 950
形成字符串的最短路径

形成字符串的最短路径

难度:

标签:

题目描述

代码结果

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


/*
 * Problem 1055: Shortest Way to Form String
 * 
 * Given a string source and a string target, return the minimum number of subsequences of source such that their concatenation equals target. 
 * If the task is impossible, return -1.
 * 
 * Approach using Java Streams:
 * 1. Stream through the target string and try to match characters in the source using IntStream.
 * 2. Use nested IntStream loops to check for character matches.
 * 3. Return -1 if no progress is made in matching.
 */
import java.util.stream.IntStream;

public class Solution {
    public int shortestWay(String source, String target) {
        int[] result = {0};  // To store number of subsequences used
        int i = 0;  // Pointer for target
        while (i < target.length()) {
            int start = i;  // To track progress in target
            i = IntStream.range(i, target.length())
                    .filter(idx -> IntStream.range(0, source.length())
                    .anyMatch(j -> source.charAt(j) == target.charAt(idx)))
                    .findFirst()
                    .orElse(target.length());
            if (start == i) {  // No progress in target
                return -1;
            }
            result[0]++;
        }
        return result[0];
    }
}

解释

方法:

该题解利用了一个贪心算法的思想,主要目标是使用给定的source字符串尽可能多地形成target字符串。首先,创建一个字典dic,里面包含26个空列表,分别对应26个字母的索引。遍历source字符串,将每个字符的索引按字母顺序存储到相应的列表中。对于target中的每个字符,如果在source中找不到该字符,则直接返回-1,表示无法形成。如果找到了,使用二分搜索来寻找可以接上的最小的索引。如果找到了更大的索引,就继续;如果没有找到,表示当前source的遍历已经结束,需要重新开始遍历source,此时计数器ans加1。最后,返回使用source字符串形成target所需的最少次数。

时间复杂度:

O(n + m*log(n))

空间复杂度:

O(n)

代码细节讲解

🦆
在构建索引列表时,你选择了一个包含26个空列表的字典来存储字符索引。请问为什么选择这种数据结构而不是其他类型的数据结构?
选择包含26个空列表的字典(实际上是列表的列表,因为Python中没有严格的字典类型用于此用途)来存储每个字母的索引是因为这样可以快速、直接地访问每个字符的所有出现位置。这种数据结构使得查找和更新操作都非常高效。使用列表的列表而不是哈希表或其他数据结构,是因为字符集限定在26个小写英文字母,可以直接通过字符的ASCII值计算索引,这样做既简单又避免了额外的哈希计算开销,提高了索引和查找的速度。
🦆
在遍历target字符串时,你使用了二分搜索来查找可以接上的最小的索引。请问在这里使用二分搜索的具体原因是什么?
在遍历target字符串时,使用二分搜索来查找可以接上的最小的索引是因为每个字符对应的索引列表是按照在source中的出现顺序(即升序)排序的。通过二分搜索,我们可以快速找到当前字符在source中的下一个可用位置,这样可以有效地减少查找时间,特别是当source字符串较长时。二分搜索的时间复杂度为O(log n),比线性搜索O(n)更高效,能够加快整体算法的执行速度。
🦆
你提到如果在遍历target时,没有找到合适的索引就需要重新从source的开始位置进行遍历,这个过程是如何确保总体最短路径的?
重新从source的开始位置进行遍历的逻辑是基于贪心算法的思想。每当在当前的source遍历中无法找到target的下一个字符时,意味着必须重新从source的开头开始搜索以继续匹配target。这样做确保了每次都能尽可能多地在当前的source遍历中匹配target中的字符,从而实现在最少的source重复次数中完成target的构建。虽然这种方法不一定保证是绝对的最短路径(在所有可能的source重排中),但在不改变source顺序的前提下,是实现目标的有效且实用的方法。

相关问题

判断子序列

给定字符串 st ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

进阶:

如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

致谢:

特别感谢 @pbrother 添加此问题并且创建所有测试用例。

 

示例 1:

输入:s = "abc", t = "ahbgdc"
输出:true

示例 2:

输入:s = "axc", t = "ahbgdc"
输出:false

 

提示:

  • 0 <= s.length <= 100
  • 0 <= t.length <= 10^4
  • 两个字符串都只由小写字符组成。