[LeetCode] Word Break

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),时间复杂度上差得还是很多的。


另外,发现网上还有一种构造字典树解决的方法,http://www.iteye.com/topic/1132188#2402159。


你可能感兴趣的:(LeetCode,break,word)