Given a list of words (without duplicates), please write a program that returns all concatenated words in the given list of words.
A concatenated word is defined as a string that is comprised entirely of at least two shorter words in the given array.
Example:
Input: ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"] Output: ["catsdogcats","dogcatsdog","ratcatdogcat"] Explanation: "catsdogcats" can be concatenated by "cats", "dog" and "cats";
"dogcatsdog" can be concatenated by "dog", "cats" and "dog";
"ratcatdogcat" can be concatenated by "rat", "cat", "dog" and "cat".
Note:
- The number of elements of the given array will not exceed
10,000
- The length sum of elements in the given array will not exceed
600,000
. - All the input string will only include lower case letters.
- The returned elements order does not matter.
這道題給了一個由單詞組成的數組,某些單詞是可能由其他的單詞組成的,讓我們找出所有這樣的單詞。這道題跟之前那道Word Break十分類似,我們可以對每一個單詞都調用之前那題的方法,我們首先把所有單詞都放到一個unordered_set中,這樣可以快速找到某個單詞是否在數組中存在。對於當前要判斷的單詞,我們先將其從set中刪去,然后調用之前的Word Break的解法,具體講解可以參見之前的帖子。如果是可以拆分,那么我們就存入結果res中,參見代碼如下:
解法一:
class Solution { public: vector<string> findAllConcatenatedWordsInADict(vector<string>& words) { if (words.size() <= 2) return {}; vector<string> res; unordered_set<string> dict(words.begin(), words.end()); for (string word : words) { dict.erase(word); int len = word.size(); if (len == 0) continue; vector<bool> v(len + 1, false); v[0] = true; for (int i = 0; i < len + 1; ++i) { for (int j = 0; j < i; ++j) { if (v[j] && dict.count(word.substr(j, i - j))) { v[i] = true; break; } } } if (v.back()) res.push_back(word); dict.insert(word); } return res; } };
下面這種方法跟上面的方法很類似,不同的是判斷每個單詞的時候不用將其移除set,而是在判斷的過程中加了判斷,使其不會判斷單詞本身是否在集合set中存在,而且由於對單詞中子字符串的遍歷順序不同,加了一些優化在里面,使得其運算速度更快一些,參見代碼如下:
解法二:
class Solution { public: vector<string> findAllConcatenatedWordsInADict(vector<string>& words) { vector<string> res; unordered_set<string> dict(words.begin(), words.end()); for (string word : words) { int n = word.size(); if (n == 0) continue; vector<bool> dp(n + 1, false); dp[0] = true; for (int i = 0; i < n; ++i) { if (!dp[i]) continue; for (int j = i + 1; j <= n; ++j) { if (j - i < n && dict.count(word.substr(i, j - i))) { dp[j] = true; } } if (dp[n]) {res.push_back(word); break;} } } return res; } };
下面這種方法是遞歸的寫法,其中遞歸函數中的cnt表示有其他單詞組成的個數,至少得由其他兩個單詞組成才符合題意,參見代碼如下:
解法三:
class Solution { public: vector<string> findAllConcatenatedWordsInADict(vector<string>& words) { vector<string> res; unordered_set<string> dict(words.begin(), words.end()); for (string word : words) { if (word.empty()) continue; if (helper(word, dict, 0, 0)) { res.push_back(word); } } return res; } bool helper(string& word, unordered_set<string>& dict, int pos, int cnt) { if (pos >= word.size() && cnt >= 2) return true; for (int i = 1; i <= (int)word.size() - pos; ++i) { string t = word.substr(pos, i); if (dict.count(t) && helper(word, dict, pos + i, cnt + 1)) { return true; } } return false; } };
類似題目:
參考資料:
https://discuss.leetcode.com/topic/72393/c-772-ms-dp-solution