如何验证最长拼接单词是否完全由字典内短单词构成?
如何检查目标单词是否由字典中更短单词拼接而成
嘿,我来帮你搞定这个问题!首先得指出,你当前的tryMatch方法存在几个关键的逻辑问题,咱们先把这些坑填上,再给你靠谱的实现思路和代码。
你的现有方法的问题
- 字符串比较错误:Java里用
nextWord == s判断字符串相等是踩坑了——==比的是对象引用,不是内容,得用equals()才行。 - 越界风险:当
i是字典最后一个元素时,i+1直接会抛出IndexOutOfBoundsException,这绝对会让程序崩溃。 - 核心逻辑不对:
contains()只能判断子串存在,但没法保证这些子串能无重叠、完整拼接出目标单词。举个例子:如果目标单词是"applepie",字典里有"app"和"pie",你的方法会返回true,但实际上"app"+"pie"拼出来是"apppie",根本不是"applepie",但你的方法会误判。 - 统计逻辑无意义:
contaned >1的判断完全站不住脚——比如一个单词可能只需要两个短单词拼接(比如"a"+"b"拼成"ab"),但也可能需要多个,更关键的是,就算统计到多个子串存在,也不代表它们能凑成完整的目标单词。
正确的实现思路
既然你的字典已经按长度降序排列,咱们可以利用这个特性优化检查过程:
- 先把字典转成
HashSet,这样判断某个单词是否存在的时间复杂度是O(1),效率高很多。同时要排除目标单词本身,因为题目要求用更短的单词拼接。 - 用两种靠谱的方式实现检查:
- 递归+记忆化:遍历所有可能的拆分点,检查前缀是否在字典里,然后递归检查剩下的后缀能不能被拆分。用记忆化缓存避免重复计算,提升效率。
- 动态规划:用一个布尔数组记录子串是否能被拼接,从短到长逐步推导整个单词是否符合要求,适合处理较长的单词,不会有栈溢出问题。
代码实现
方法1:递归+记忆化(适合单词长度不算特别长的场景)
import java.util.List; import java.util.Set; import java.util.HashSet; public class Solution { private Set<String> wordSet; private Boolean[] memo; public boolean tryMatch(String s, List<String> dictionary) { // 转成Set方便快速查找,同时移除目标单词本身 wordSet = new HashSet<>(dictionary); wordSet.remove(s); // 记忆化数组,缓存子问题的结果 memo = new Boolean[s.length() + 1]; return canSplit(s, 0); } private boolean canSplit(String s, int start) { // 走到字符串末尾,说明拼接成功 if (start == s.length()) { return true; } // 已经计算过这个子问题,直接返回缓存结果 if (memo[start] != null) { return memo[start]; } // 遍历所有可能的拆分点 for (int end = start + 1; end <= s.length(); end++) { String prefix = s.substring(start, end); // 前缀在字典里,且后缀也能被拆分的话,就返回true if (wordSet.contains(prefix) && canSplit(s, end)) { memo[start] = true; return true; } } // 所有拆分方式都不行,标记为false memo[start] = false; return false; } }
方法2:动态规划(适合长单词,避免递归栈溢出)
import java.util.List; import java.util.Set; import java.util.HashSet; public class Solution { public boolean tryMatch(String s, List<String> dictionary) { Set<String> wordSet = new HashSet<>(); // 只保留比目标单词短的单词,减少集合大小 for (String word : dictionary) { if (word.length() < s.length()) { wordSet.add(word); } } int n = s.length(); // dp[i]表示s的前i个字符组成的子串能否被拼接 boolean[] dp = new boolean[n + 1]; dp[0] = true; // 空串是基础情况,默认可以拼接 for (int i = 1; i <= n; i++) { for (int j = 0; j < i; j++) { // 如果前j个字符能拼接,且j到i的子串在字典里,就标记dp[i]为true if (dp[j] && wordSet.contains(s.substring(j, i))) { dp[i] = true; break; // 找到一种方式就够了,不用继续遍历 } } } return dp[n]; } }
额外优化小技巧
因为你的字典已经按长度降序排列,在构建wordSet的时候,我们可以直接跳过长度大于等于目标单词的单词(除了目标单词本身,不过我们已经排除了),这样能减少集合的大小,提升查找和遍历的效率,就像上面动态规划代码里做的那样。
内容的提问来源于stack exchange,提问作者Timoha Timoshenko
相关产品推荐
相关产品推荐

