LeetCode: Word Break I && II

I

第一问上来以为是简单的考察DFS搜索,果断敲了一发结果TLE。看了一眼Tags提示,原来是个DP问题。状态表示很简单,设dp[i]表示前i个字符是否能够由字典中的单词组成,则很容易由dp[i-1]推出dp[i]。核心思路是,最后第i个字符,可以尝试自己组成一个单词、与第i-1个字符组成单词、与后两个字符组成单词……一直到尝试前i个字符能否组成单独一个字典中的单词。这些尝试中只要有一个成功,就可以标记dp[i]=true

II

第二问是第一问的进一步加深,要求输出所有可能的组成情况。还是使用递归回溯的思路,首先我们在DP的过程中记录当前状态是由上次的哪个状态转移而来,然后从末状态执行一次DFS,搜索到起始状态,这个过程中就可以找出所有可能的解。由于先前的DP已经进行了预处理,搜索的过程中每次DFS一定都可以抵达终点,也就是说,每次搜索必然是找到一个可行解,因此这个算法的时间复杂度是最低的,恰好找出了所有答案。

代码

class Solution {
public:
    static const int MAX = 2000;
    int dp[MAX];
    vector<int> prev[MAX];
    vector<string> wordBreak(string s, unordered_set<string> &dict) {
        vector<string> ans;
        vector<string> result;
        for ( int i = 0; i < s.size(); i++ ) prev[i].clear();
        dp[0] = dict.find(s.substr(0, 1)) != dict.end();
        prev[0].push_back(-1);
        for ( int i = 1; i < s.length(); i++ ) {
            string str;
            str += s[i];
            for ( int j = i - 1; j >= -1; j-- ) {
                if ( (j == -1 || dp[j]) && dict.find(str) != dict.end() ) {
                    dp[i] = true;
                    prev[i].push_back(j);
                }
                str = s[j] + str;
            }
        }
        if ( dp[s.size() - 1] ) DFS(s.size() - 1, ans, result, s);
        return result;
    }
    void DFS( int pos, vector<string> & ans,vector<string> & result, string & s ) {
        if ( pos == -1 ) {
            string cur;
            for ( int i = ans.size() - 1; i >= 0; i-- ) {
                cur += ans[i];
                if ( i ) cur += ' ';
            }
            result.push_back(cur);
            return;
        }
        for ( int i = 0; i < prev[pos].size(); i++ ) {
            int len = pos - ( prev[pos][i] + 1 ) + 1;
            int start = prev[pos][i] + 1;
            ans.push_back(s.substr(start, len));
            DFS(prev[pos][i], ans, result, s);
            ans.pop_back();
        }
    }
};
comments powered by Disqus
Published:
2015-01-18
分类:
Tag:
DP15