leetcode
leetcode 451 ~ 500
通过删除字母匹配到字典里最长单词

通过删除字母匹配到字典里最长单词

难度:

标签:

题目描述

给你一个字符串 s 和一个字符串数组 dictionary ,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。

如果答案不止一个,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字符串。

 

示例 1:

输入:s = "abpcplea", dictionary = ["ale","apple","monkey","plea"]
输出:"apple"

示例 2:

输入:s = "abpcplea", dictionary = ["a","b","c"]
输出:"a"

 

提示:

  • 1 <= s.length <= 1000
  • 1 <= dictionary.length <= 1000
  • 1 <= dictionary[i].length <= 1000
  • sdictionary[i] 仅由小写英文字母组成

代码结果

运行时间: 48 ms, 内存: 18.1 MB


/*
 * 问题描述:
 * 给定一个字符串 s 和一个字符串数组 dictionary,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。
 * 如果有多个答案,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字符串。
 *
 * 思路:
 * 1. 使用流操作遍历字典中的每个单词。
 * 2. 过滤出可以通过删除 s 中的字符得到的单词。
 * 3. 找到满足条件的最长单词,如果长度相同,则字母序更小的单词。
 */
 
import java.util.List;
import java.util.stream.Collectors;
 
public class Solution {
    public String findLongestWord(String s, List<String> dictionary) {
        return dictionary.stream()
                .filter(word -> isSubsequence(word, s))
                .sorted((a, b) -> b.length() == a.length() ? a.compareTo(b) : b.length() - a.length())
                .findFirst()
                .orElse("");
    }
 
    private boolean isSubsequence(String word, String s) {
        int i = 0, j = 0;
        while (i < word.length() && j < s.length()) {
            if (word.charAt(i) == s.charAt(j)) {
                i++;
            }
            j++;
        }
        return i == word.length();
    }
}

解释

方法:

该题解的思路是利用动态规划预处理字符串 s,为每个字符建立一个长度为 26 的数组,记录每个字母下一次出现的位置。然后对于字典中的每个单词,逐个字符在预处理数组中匹配,如果能够匹配到单词的所有字符,则说明该单词可以通过删除 s 中的某些字符得到。最后从所有匹配成功的单词中选择长度最长且字母序最小的单词作为答案。

时间复杂度:

O(m + kn)

空间复杂度:

O(m)

代码细节讲解

🦆
在预处理数组中,为什么需要在最后添加一个长度为26的数组元素,其值都是m?
在预处理数组中添加一个长度为26的数组元素,且其每个位置的值都是m,是为了处理边界情况,即当某个字符在当前位置之后不再出现时。这样设置可以确保在使用预处理数组进行字符查找时,若当前字符在后续位置中不存在,数组会返回一个大于字符串s长度的值m,从而使得循环可以正常终止。这样的处理避免了额外的边界检查,简化代码逻辑。
🦆
你是如何确保在遍历dictionary中的单词时,每个字符的查找都是有效的,尤其是当字符在字符串s中不存在时?
通过预处理数组,每个字符位置记录了在字符串s中该字符下一次出现的位置。如果某个字符在s中不存在,对应的预处理数组的值将会是m(s的长度)。在遍历字典中的单词时,如果遇到某个字符在s中不存在,预处理数组会提供一个值m,这个值用于终止当前单词的匹配过程,因为它超出了s的范围。这保证了每次字符的查找都有效且能正确处理字符不存在的情况。
🦆
为什么在遍历单词t的每个字符后,使用`else`子句来处理匹配成功的逻辑?这种方法有什么特别的优势吗?
在Python中,`for-else`结构中的`else`部分会在`for`循环正常结束后执行,而不是因为`break`而中断时执行。这种结构在这里被用来检查是否每个字符都成功匹配到了s中的对应字符。如果所有字符都匹配成功(即没有触发`break`),则执行`else`子句中的代码来更新结果。这样的设计减少了额外的状态标志或检查,使得代码更为简洁和直观。
🦆
在比较两个单词长度相同但字母序不同时,你是如何决定哪一个单词应该作为最终结果的?具体的比较逻辑是什么样的?
在确定最终结果的单词时,首先比较两个单词的长度,长度较长的单词优先。如果长度相同,则比较两个单词的字母序。在Python中,字符串的比较是按字典顺序进行的,因此,如果单词t的字母序小于当前结果res的字母序(即`t < res`),则t会替换res作为新的结果。这样的比较逻辑确保了在长度相同的情况下,字典序较小的单词被选择。

相关问题

词典中最长的单词

给出一个字符串数组 words 组成的一本英语词典。返回 words 中最长的一个单词,该单词是由 words 词典中其他单词逐步添加一个字母组成。

若其中有多个可行的答案,则返回答案中字典序最小的单词。若无答案,则返回空字符串。

 

示例 1:

输入:words = ["w","wo","wor","worl", "world"]
输出:"world"
解释: 单词"world"可由"w", "wo", "wor", 和 "worl"逐步添加一个字母组成。

示例 2:

输入:words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
输出:"apple"
解释:"apply" 和 "apple" 都能由词典中的单词组成。但是 "apple" 的字典序小于 "apply" 

 

提示:

  • 1 <= words.length <= 1000
  • 1 <= words[i].length <= 30
  • 所有输入的字符串 words[i] 都只包含小写字母。