单词拆分
- 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例 1:
输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以由 “leet” 和 “code” 拼接成。
解题思路
这个问题可以使用动态规划来解决。
-
1、我们定义一个长度为 s.length()+1 的布尔数组 dp,其中 dp[i] 表示字符串 s 的前 i 个字符是否可以被字典中的单词拼接而成。
动态规划的状态转移方程为: -
dp[i] = dp[j] && wordDict.contains(s.substring(j, i)),其中 0 <= j < i
这个方程的意思是,如果存在一个 j,使得 dp[j] 为 true并且字典中包含 s.substring(j, i), 那么 dp[i] 就可以被设置为 true,表示字符串 s 的前 i 个字符可以被拼接而成。
-
2、初始化数组dp,长度为s.length() + 1,全部初始化为false。
-
3、设置dp[0]为true,表示空字符串可以被拆分。
-
4、使用两层循环遍历字符串s的每个字符,外层循环遍历从1到s.length(),内层循环遍历从0到i,判断从0到j的子串是否可以被拆分,并且判断从j到i的子串是否在wordDict中,如果满足条件,则将dp[i]设置为true。
-
5、最终返回dp[s.length()]即可。
Java实现
public class WordBreak {public boolean wordBreak(String s, List<String> wordDict) {Set<String> wordSet = new HashSet<>(wordDict);int n = s.length();boolean[] dp = new boolean[n + 1];dp[0] = true;for (int i = 1; i <= n; i++) {for (int j = 0; j < i; j++) {if (dp[j] && wordSet.contains(s.substring(j, i))) {dp[i] = true;break;}}}return dp[n];}public static void main(String[] args) {WordBreak wb = new WordBreak();String s = "leetcode";List<String> wordDict = Collections.unmodifiableList(Arrays.asList("leet", "code"));System.out.println("Can be broken: " + wb.wordBreak(s, wordDict));// Output: true ("leetcode" can be broken into "leet" and "code")}
}
时间空间复杂度
-
时间复杂度:外层循环遍历了s.length()次,内层循环遍历了字符串s的每个字符,时间复杂度为O(n^2),其中n为字符串s的长度。
-
空间复杂度:使用了长度为s.length() + 1的数组dp和一个哈希集合wordSet,空间复杂度为O(n)。