Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
For example, given
s = "leetcode"
,
dict = ["leet", "code"]
.
Return true because "leetcode"
can be segmented as "leet code"
.
这题使用简单的递归解法会超时,这个时候一般有两种选择:
1)在递归之前做些pruning,减少递归的发生,甚至可以在DFS的时候对子问题分支设置不同优先级,对一些深度较小的分支进行优先递归。(当然这种懒人的做法不一定总是有效,而且可能需要多次尝试);
2)使用DP,减少对同一个子问题的重复递归求解。
沿着第一种思路的改进:
1)如果给出的词中存在某个字符不在字典的字符集中,那么可以直接否定。
2) 测试用例s="aaa....a", dict = {"a", "aa", ..., "aaaaaaa" }得到启发,可以在做深度遍历的时候优先从长度最大的字典词入手。这样需要对所有的字典词进行统计,记录最长的词的长度,和最短的词的长度。
private boolean dfs(String s, Set<String> dict, int min, int max) { // exit if ("".equals(s)) return true; for (int i = Math.min(max, s.length()); i >= min; i--) { if (dict.contains(s.substring(0, i))) { if (dfs(s.substring(i), dict, min, max)) return true; } } return false; } public boolean wordBreak(String s, Set<String> dict) { // the minimum and maximum length of the dictionary word int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; // the character set of the dictionary Set<Character> charSet = new HashSet<Character>(); for (String str : dict) { for (int i = 0; i < str.length(); i++) { charSet.add(str.charAt(i)); } min = Math.min(min, str.length()); max = Math.max(max, str.length()); } // check if all the characters in the given string is contained by the // character set for (int i = 0; i < s.length(); i++) { if (!charSet.contains(s.charAt(i))) return false; } return dfs(s, dict, min, max); }
如果让seg(i, j)为起始点为i,长度为j的substring,如果seg(i, j)可以被拆解,必须至少符合以下两种情况之中的一种:
1)自身是否是字典词;
2)对于任意的k (1<=k<j),是否存在seg(i, k)和seg(i + k, j - k)同时都可以被拆解。
public boolean wordBreak(String s, Set<String> dict) { if ("".equals(s)) return true; // seg[i][0] is wasted here boolean seg[][] = new boolean[s.length()][s.length() + 1]; for (int len = 1; len <= s.length(); len++) { for (int i = 0; i < s.length(); i++) { if (i + len <= s.length()) { // 1) seg(i, len) is a dictionary word if (dict.contains(s.substring(i, i + len))) { seg[i][len] = true; continue; } // 2) seg(i, k) and seg(i + k, len - k) can be segmented for (int k = 1; k < len; k++) { if (seg[i][k] && seg[i + k][len - k]) { seg[i][len] = true; break; } } } } } return seg[0][s.length()]; }
递归做法的时间复杂度是O(2^N),DP做法时间复杂度是O(N^3),时间复杂度上差得还是很多的。