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"
.
第一种思路:
使用递归方法
public class Solution { public boolean wordBreak(String s, Set<String> dict) { Iterator<String> iterator = dict.iterator(); while(iterator.hasNext()){ String tmp = iterator.next(); if(s.indexOf(tmp) > -1){//s中存在tmp,可以分割 String[] sub = s.split(tmp); int count = 0; for(int i = 0; i < sub.length; i ++){ if(wordBreak(sub[i],dict)){ count++; } } if(count == sub.length){ return true; } } } return false; } }
Time Limit Exceeded
分析可知,递归很耗费时间!
public class Solution { public boolean wordBreak(String s, Set<String> dict) { int len = s.length(); int[][] dp = new int[len][len]; for(int i = 0 ; i < len ; i ++){ for(int j = i ; j < len ; j ++){ String tmp = s.substring(i,j+1); if(dict.contains(tmp)){ dp[i][j] = 1;//(i~j)是一个字典项 } } } return path(dp,0); } private boolean path(int[][] dp ,int index){ if(index >= dp[0].length){ return true; } boolean flag = false; for(int i = index ; i < dp[0].length; i ++){ if(dp[index][i] == 1){ if(path(dp,i+1)){ return true; } } } return false; } }
思想是先寻找(i,j)是否在字典里,这样可以使用O(n2)的时间创建一副“地图“出来
然后再从地图中找到一条路使得(0,n)无重复节点。
Time Limit Exceeded
string=“aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab“
dict:["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"]
dp[152*152] =
1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
............
我们发现要递归10*152+才能发现不可分割,
而实际上我们如果注意到了“b“,那么就可以很快的确定不可分割,因为“b“在字典里没有出现
public class Solution { public boolean wordBreak(String s, Set<String> dict) { //验证是否s里存在dict不存在的字符 int[] c = new int[26]; for(int i = 0; i < s.length(); i ++){ c[s.charAt(i)-'a'] = 1; } //for(int i = 0 ; i < 26 ; i ++){ // System.out.println((char)(i+'a')+":" + (int)c[i]); //} for(int i = 0; i < 26 ; i ++){ if(c[i] == 1){ int count = 0; Iterator<String> ite = dict.iterator(); while(ite.hasNext()){ if(ite.next().indexOf((char)(i+'a')) < 0){ count++; } } if(count == dict.size()){ System.out.println("here"); return false; } } } //只有一个字符 if(s.length() == 1){ return true; } int len = s.length(); int[][] dp = new int[len][len]; for(int i = 0 ; i < len ; i ++){ for(int j = i ; j < len ; j ++){ String tmp = s.substring(i,j+1); if(dict.contains(tmp)){ dp[i][j] = 1;//(i~j)是一个字典项 } } } return path(dp,0); } private boolean path(int[][] dp ,int index){ if(index >= dp[0].length){ return true; } boolean flag = false; for(int i = index ; i < dp[0].length; i ++){ if(dp[index][i] == 1){ if(path(dp,i+1)){ return true; } } } return false; } }
string="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab", dict:["a","aa","ba"]
Time Limit Exceeded
看来只能另辟蹊径了。
原文 http://blog.csdn.net/ljphhj/article/details/21643391
核心思想是F(0,n)= F(0,i-1)+ F(i,j-1)+F(j,n)
public class Solution { public boolean wordBreak(String s, Set<String> dict) { boolean flag = false; List<Integer> list = new ArrayList<Integer>();//存储所有可能的从i到n的情况 int len = s.length(); for(int i = len - 1 ; i >= 0 ; i --){ String tmp = s.substring(i); if(dict.contains(tmp)){//i~n是一个字典项 list.add(i); }else{//i~n不是一个字典项,那么查找是否存在j,使得F(i,n)=F(i,j)+ F(j+1,n) for(Integer n : list){ if(dict.contains(s.substring(i,n))){//实际查询F(i,n-1)是否存在,所以不能是substring(i,n+1) list.add(i); break; } } } } if(list.size()==0){ flag = false; }else{ if(list.get(list.size()-1) == 0){ flag = true; }else{ flag = false; } } return flag; } }
string = "leetcode"
dict=["leet","co","de","code"]
通过上述程序,list存着什么?
list=[6,4,0]
string = "leetcode"
dict=["leet","de","code"]
通过上述程序,list存着什么?
list=[6,4,0]
思考可知:这两处的4是不同含义
第一个4指的是{4,6},而第二个4是{4,n}
也就是说上述程序的list只是找到从i到n可以使用dict分割,至于如何分割,还得分析。
public class Solution { public boolean wordBreak(String s, Set<String> dict) { int len = s.length(); boolean[] arrays = new boolean[len+1]; arrays[0] = true; for (int i = 1; i <= len; ++i){ for (int j = 0; j < i; ++j){ if (arrays[j] && dict.contains(s.substring(j, i))){ // f(n) = f(0,i) + f(i,j) + f(j,n) arrays[i] = true; //到i是可分割的 break; } } } return arrays[len]; } }
分析上述两个程序可知,思想是一致的,只不过是一个从前往后思考,一个是从后往前思考。