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".

http://www.lintcode.com/en/problem/word-break/

Discussion


Very classical DP.
f[i] is true means sub-string [0,i] can be segmented. f[i] = true when [0,i] belongs to dictionary or existing a j,0<=j<i, f[j]&&[j+1, i] belongs to dictionary.

Solution


class Solution {
public:
    bool wordBreak(string s, unordered_set<string>& wordDict) {
        int n = s.length();
        vector<bool> f(n, false);
        if(n==0) return true;
        string sub = s.substr(0,1);
        if(wordDict.find(sub) != wordDict.end())
            f[0] = true;
        else
            f[0] = false;
        for(int i=1; i<n; i++) {
            if( wordDict.find(s.substr(0, i+1)) != wordDict.end()) {
                f[i] = true;//先判断[0,i]是不是属于dictionary,是的话就不用找j了。如果先找j,找不到的时候再判断效率低很多。
                continue;
            }
            for(int j=i-1; j>=0; j--) {
                if(f[j] && wordDict.find(s.substr(j+1, i-j)) != wordDict.end()) {
                    f[i] = true;
                    break;
                }
            }
        }
        return f[n-1];
    }
};

上面的solution在leetcode上accepted,但是在lintcode上time limited。。。
时间复杂度O(n^2),空间复杂度O(n)

Solution 2


利用字典里面的单词的最大长度(假设为M),可以将复杂度降低为O(Mn)。

用f[i]表示前i个字符能break,代码更好写。

class Solution {
public:
    /**
     * @param s: A string s
     * @param dict: A dictionary of words dict
     */
    bool wordBreak(string s, unordered_set<string> &dict) {
        // write your code here
        if(s.length()==0 ) return true;
        int n = s.length();
        vector<bool> f(n+1, false);
        f[0] = true;
        int maxLen = 0;
        for(unordered_set<string>::iterator it = dict.begin(); it!=dict.end(); it++) {
            int tmp = (*it).length();
            if(tmp > maxLen) {
                maxLen = tmp;
            }
        }
        for(int i=1; i<=n; i++) {
            for(int j=i-1; j>=0; j--) {//这里一定要自底向上才行
                if(i-j> maxLen) {//这里降低复杂度,利用maxLen
                    break;
                }
                if(f[j] && dict.find(s.substr(j, i-j)) != dict.end()) {
                    f[i] = true;
                    break;
                }
            }
        }
        return f[n];
    }
};

Solution 3 DFS

这个题也可以用DFS实现,每个单词哪里要么break要么不break,根据ABCD画出状态转移图,起始就是个有向图。问题转换成有向图中路径是否存在问题。关键在于找一个节点的neighbors怎么找。O(2^n)复杂度,提交超时。

//O(2^n) rumtime
class Solution {
public:
    /**
     * @param s: A string s
     * @param dict: A dictionary of words dict
     */
    bool wordBreak(string s, unordered_set<string> &dict) {
        if(s.length()==0 && dict.empty()) return true;
        if(s.length() == 0) return false;
        return breakable(s, dict, 0);

    }
    //start is the starting positoin of next level, it's also the length of the path
    bool breakable(string s, unordered_set<string> &dict, int start) {
        int n = s.length();
        if(start == n) return true;
        vector<string> words = GetNeighbors(s, start, dict);
        for(auto const& word : words) {
            if(breakable(s, dict, start+word.length())) {
                return true;
            }
        }
        return false;

    }
    //return all the substrings staring from pos
    vector<string> GetNeighbors(string s, int pos, unordered_set<string> &dict) {
        vector<string> result;
        string sub;
        for(int i=pos; i<s.length(); i++) {
            sub.append(1, s[i]);
            if(dict.find(sub) != dict.end())
                result.emplace_back(sub);
        }
        return result;
    }
};

每个单词哪里要么break要么不break,这个思路在String Palindrome那个题里面也是一样的,所以DFS的思路也可以一样. 上面说状态转移图是个有向图,起始更严格的说是个树

class Solution {
public:
    /**
     * @param s: A string s
     * @param dict: A dictionary of words dict
     */
    bool wordBreak(string s, unordered_set<string> &dict) {
        if(s.empty()) return true;
        int n = s.length();
        return isBreakable(s, 0, dict);
    }
    bool isBreakable(string s, int pos, unordered_set<string> &dict) {
        if(pos == s.length()) {
            return true;
        }
        for(int i=pos; i<s.length(); i++) {
            if(dict.find(s.substr(pos, i-pos+1)) == dict.end()) {
                continue;
            }
            if(isBreakable(s, i+1, dict)) {
                return true;
            }
        }
        return false;
    }
};

results matching ""

    No results matching ""