LeetCode 139 Word Break
2014-02-25 11:31
585 查看
题目
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 =
dict =
Return true because
判断一个字符串,能否被字典里的字符串所切割?
思路1
1.1暴力解法。把每种情况都考虑。比如”ab”,可能“a b”或者”ab”。
1.2 时间复杂度来说,可以考虑每个字符可能与前一个相连或者不相连,所以n个字符,可以有2^n的可能性,也就是O(2^n)。证明时间复杂度可以用数学归纳法证明。
代码1
思路2
1 时间不行,有很多重复检查了。那么考虑从底向上的字典dfs。需要一个二维数组mp。
2 mp[i][j] 代表 S 从i 到j 是否可以匹配。只要存在k mp[i][k] 和 mp[k][j] 都为true的时候,mp[i][j] 也为true。
3 我们需要求的就是mp[0][n-1]
4 时间是O(n3)
代码2
思路3
1 虽然accept,但是时间不够好。继续思考。
2 发现其实我们要求的只是ans[0][n-1]。然后对于每个0行的元素,也就是string 0--i,只有在他之前的0-j是true和ans[j][i]是true的时候才true。
3 所以我们只要再多一个memo[n+1]的数组,来记录每个0-j是否为true,结合上面的ans数组,就可以判断了。
4 时间缩小到O(n2), dp的技巧让 空间换时间。
代码3
思路4
4.1我称之为“字典贪婪”解法。也是需要一个一维数组,来表示前n个字符串是否可以切分。从第一个字符开始,每次遍历所有字典,把所有可能到达的字符串的位子都在数组中标示出来。然后再检查下一个字符,如果false,表示不能到达它,就不用看了;如果true,继续如此。
4.2 考虑时间复杂度。假设最差情况每个字符都要检查,然后字典含量为X,那么时间复杂度就是(Xn)。
4.3 当字典的内容远远少于字符串的长度的时候,可以考虑使用这种算法
代码4
备注
1其中思路2和思路3 ,我称之为“贪婪字符串”解法。”abcd“,从最后一个字符考虑。如果可以切分,那么
2.1.1要么”d”字典有,并且”abc”的某种切分成立;
2.1.2要么”cd”字典有,并且”ab”的某种切分成立;
2.1.3 要么“bcd”字典有,并且“a”的某种切分成立;
2.1.4 要么“abcd”字典有,并且“”的某种切分成立;
可以看到之前的切分也可以用这个方法来完成,并且有循环的含义在里面。所以可以使用递归或者循环来实现。考虑用一个数组来表示到某个单词为止可以实现切分,即能完成此类算法。
3 考虑时间复杂度。可以看到在前1个字符的时候,检查1种可能;前2个字符的时候,检查2种可能。。。前n个字符的时候,检查n种可能。于是1+2+。。。+n: O(n^2)的时间复杂度。
4 这个时间复杂度是可以接受的。
5 另外一种写法:
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
1.1暴力解法。把每种情况都考虑。比如”ab”,可能“a b”或者”ab”。
1.2 时间复杂度来说,可以考虑每个字符可能与前一个相连或者不相连,所以n个字符,可以有2^n的可能性,也就是O(2^n)。证明时间复杂度可以用数学归纳法证明。
代码1
public boolean wordBreak(String s, Set<String> dict) { return worddfs(s,dict); } public boolean worddfs(String s,Set<String> dict){ if(s.equals("")){ return true; } for(int i=0;i<s.length();i++){ String temp = s.substring(0,i+1); if(dict.contains(temp)){ if(i==s.length()-1){ return true; } if(worddfs(s.substring(i+1),dict)){ return true; } } } return false; }
思路2
1 时间不行,有很多重复检查了。那么考虑从底向上的字典dfs。需要一个二维数组mp。
2 mp[i][j] 代表 S 从i 到j 是否可以匹配。只要存在k mp[i][k] 和 mp[k][j] 都为true的时候,mp[i][j] 也为true。
3 我们需要求的就是mp[0][n-1]
4 时间是O(n3)
代码2
public class Solution { public boolean wordBreak(String s, Set<String> dict) { if(s.length()==0){ return false; } int n = s.length(); boolean[][] ans = new boolean ; for(int i=0;i<n;i++){ for(int j=i;j<n;j++){ if(dict.contains(s.substring(i,j+1))){ ans[i][j]=true; } } } for(int i=0;i<n;i++){ for(int j=i;j<n;j++){ for(int k=i;k<=j;k++){ if(!ans[i][j]){ ans[i][j]=ans[i][k]&&ans[k+1][j]; } } } } return ans[0][n-1]; } }
思路3
1 虽然accept,但是时间不够好。继续思考。
2 发现其实我们要求的只是ans[0][n-1]。然后对于每个0行的元素,也就是string 0--i,只有在他之前的0-j是true和ans[j][i]是true的时候才true。
3 所以我们只要再多一个memo[n+1]的数组,来记录每个0-j是否为true,结合上面的ans数组,就可以判断了。
4 时间缩小到O(n2), dp的技巧让 空间换时间。
代码3
public class Solution { public boolean wordBreak(String s, Set<String> dict) { if(s.length()==0){ return false; } int n = s.length(); boolean[][] ans = new boolean ; for(int i=0;i<n;i++){ for(int j=i;j<n;j++){ if(dict.contains(s.substring(i,j+1))){ ans[i][j]=true; } } } boolean[] memo = new boolean[n+1]; memo[0]=true; for(int i=0;i<n;i++){ for(int j=0;j<=i;j++){ if(memo[j]&&ans[j][i]){ memo[i+1]=true; } } } return memo ; } }
思路4
4.1我称之为“字典贪婪”解法。也是需要一个一维数组,来表示前n个字符串是否可以切分。从第一个字符开始,每次遍历所有字典,把所有可能到达的字符串的位子都在数组中标示出来。然后再检查下一个字符,如果false,表示不能到达它,就不用看了;如果true,继续如此。
4.2 考虑时间复杂度。假设最差情况每个字符都要检查,然后字典含量为X,那么时间复杂度就是(Xn)。
4.3 当字典的内容远远少于字符串的长度的时候,可以考虑使用这种算法
代码4
public class Solution { public boolean wordBreak(String s, Set<String> dict) { boolean[] canBreak = new boolean[s.length()+1]; canBreak[0] = true; for(int i=0; i<s.length(); i++){ if(canBreak[i] == false){ continue; } for(String dictS : dict){ int len = dictS.length(); int end = i + len; if(end > s.length()){ continue; } if(s.substring(i, end).equals(dictS)){ canBreak[end] = true; } } } return canBreak[s.length()]; } }
备注
1其中思路2和思路3 ,我称之为“贪婪字符串”解法。”abcd“,从最后一个字符考虑。如果可以切分,那么
2.1.1要么”d”字典有,并且”abc”的某种切分成立;
2.1.2要么”cd”字典有,并且”ab”的某种切分成立;
2.1.3 要么“bcd”字典有,并且“a”的某种切分成立;
2.1.4 要么“abcd”字典有,并且“”的某种切分成立;
可以看到之前的切分也可以用这个方法来完成,并且有循环的含义在里面。所以可以使用递归或者循环来实现。考虑用一个数组来表示到某个单词为止可以实现切分,即能完成此类算法。
3 考虑时间复杂度。可以看到在前1个字符的时候,检查1种可能;前2个字符的时候,检查2种可能。。。前n个字符的时候,检查n种可能。于是1+2+。。。+n: O(n^2)的时间复杂度。
4 这个时间复杂度是可以接受的。
5 另外一种写法:
// 2 贪婪字符串解法,使用一个数组来实现 public boolean wordBreak1_2(String str, HashSet<String> record) { if(str.length()==0){ return false; } int n = str.length(); // 检验数组,第n位就代表前n个字符代表的字符串是否可以某种方式切分 boolean[] sepHere = new boolean[n+1]; sepHere[0]=true; // i 代表前几个字符。比如i=3的时候,就代表前3个字符 for(int i=1;i<=n;i++){ // 遍历所有可能的切分方式 for(int j=i-1;j>=0;j--){ if(sepHere[j]==true && record.contains(str.substring(j,i))){ sepHere[i]=true; break; } } } return sepHere ;
相关文章推荐
- Palindrome Number
- Max Points on a Line
- Evaluate Reverse Polish Notation
- Sort List
- Insertion Sort List
- LRU Cache
- Binary Tree Postorder Traversal
- Binary Tree Preorder Traversal
- Reorder List
- Linked List Cycle
- Linked List Cycle II
- Word Break
- Word Break II
- Copy List with Random Pointer
- Single Number
- Single Number II
- Candy
- Gas Station
- Clone Graph
- Palindrome Partitioning