[leetcode]Word Break
LeetCode越来越大姨妈了,Submit上去又卡住了,先假设通过了吧。这道题拿到思考先是递归,然后有重复子状态,显然是DP。用f(i,j)表示字符串S从i到j的子串是否可分割,则有:f(0,n) = f(0,i) && f(i,n)。
但是如果自底向上求的话会计算很多不需要的,比如leet已经在字典里了,很多情况下就不需要计算下面的l,e,e,t了,所以自顶向下递归+备忘录会是更快的方法。
import java.util.*; public class Solution { private int f[][] = null; public boolean wordBreak(String s, Set<String> dict) { int len = s.length(); f = new int[len][len]; // 0 for unvisited, -1 for false, 1 for true return wordBreak(s, dict, 0, len-1); } private boolean wordBreak(String s, Set<String> dict, int i, int j) { if (f[i][j] == 1) return true; if (f[i][j] == -1) return false; String s0 = s.substring(i, j + 1); if (dict.contains(s0)) { f[i][j] = 1; return true; } for (int k = i + 1; k <= j; k++) { if (wordBreak(s, dict, i, k-1) && wordBreak(s, dict, k, j)) { f[i][j] = 1; return true; } } f[i][j] = -1; return false; } }
但是如果自底向上,状态就可以滚动数组优化少一维表示,比如下面,用wordB[i]表示从0开始长度为i的子串是否能分割。
class Solution { public: bool wordBreak(string s, unordered_set<string> &dict) { vector<bool> wordB(s.length() + 1, false); wordB[0] = true; for (int i = 1; i < s.length() + 1; i++) { for (int j = i - 1; j >= 0; j--) { if (wordB[j] && dict.find(s.substr(j, i - j)) != dict.end()) { wordB[i] = true; break; } } } return wordB[s.length()]; } };
还有一种字典树的方法,很巧妙,用个vector<bool>记录了是否能从头经过word break走到位置 i。正好练练手写写Trie树试下。http://www.iteye.com/topic/1132188#2402159
Trie树是Node且自身包含Node*的数组,如果数组某个位置不是NULL就代表此处有字符,end表示这里是一个字符串的终结。
#include <string> #include <vector> #include <unordered_set> using namespace std; class Node { public: Node* next[26]; bool end; Node() : end(false) { for (int i = 0; i < 26; i++) { next[i] = NULL; } } ~Node() { for (int i = 0; i < 26; i++) { delete next[i]; } } void insert(string s) { int len = s.length(); Node* cur = this; for (int i = 0; i < len; i++) { if (cur->next[s[i] - 'a'] == NULL) { cur->next[s[i] - 'a'] = new Node(); } cur = cur->next[s[i] - 'a']; } cur->end = true; } }; class Solution { public: bool wordBreak(string s, unordered_set<string> &dict) { Node root; int len = s.length(); vector<bool> vec(len, false); for (auto it = dict.begin(); it != dict.end(); it++) { root.insert(*it); } findMatch(s, &root, vec, 0); for (int i = 0; i < len; i++) { if (vec[i]) findMatch(s, &root, vec, i + 1); } return vec[len - 1]; } void findMatch(const string& s, Node* cur, vector<bool>& vec, int start) { int i = start; int len = s.length(); while (i < len) { if (cur->next[s[i] - 'a'] != NULL) { if (cur->next[s[i] - 'a']->end) { vec[i] = true; } cur = cur->next[s[i] - 'a']; } else break; i++; } } };
第二刷:
class Solution { public: bool wordBreak(string s, unordered_set<string> &dict) { vector<vector<int> > canBreak; // 0 for unvisited, 1 for true, -1 for false int N = s.size(); canBreak.resize(N); for (int i = 0; i < N; i++) { canBreak[i].resize(N); } return wordBreakRe(s, dict, canBreak, 0, N - 1); } bool wordBreakRe(string &s, unordered_set<string> &dict, vector<vector<int> > &canBreak, int start, int end) { if (canBreak[start][end] != 0) return (canBreak[start][end] == 1 ? true : false); string sub = s.substr(start, end - start + 1); if (dict.find(sub) != dict.end()) { canBreak[start][end] = 1; return true; } for (int i = start; i < end; i++) { if (wordBreakRe(s, dict, canBreak, start, i) && wordBreakRe(s, dict, canBreak, i + 1, end)) { canBreak[start][end] = 1; return true; } } canBreak[start][end] = -1; return false; } };
Python3
class Solution: def wordBreak(self, s: str, wordDict: List[str]) -> bool: memo = {} for i in range(len(s) + 1): # i for length of substr if i == 0: memo[i] = True else: isMatch = False for word in wordDict: if i - len(word) < 0: continue if memo[i - len(word)] and s[i - len(word): i] == word: isMatch = True break memo[i] = isMatch return memo[len(s)]