題目要求
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.
現有一個無重複的字符串數組,要求找出所有所有拼接字符串,拼接字符串是指位於該數組中可以由數組中至少兩個字符串拼接生成的字符串。
思路一:動態規劃
已知短的字符串一定無法由長的字符串構成,因此可以將字符串排序,然後逐個判斷當前字符串是否爲拼接字符串,判斷完畢後將該字符串加入字典中,作爲下一個判斷拼接字符串的可選子字符串。
這裏的自底向上動態規劃的思想用於判斷是否爲拼接字符串,即假設想要知道substring[0,i]是否可以拼接生成,並且已經知道substring[0,j]可以由其它字符串拼接而成,則只需要判斷substring[i,j]是否可以爲字典中的某個字符串即可。
代碼如下:
public List<String> findAllConcatenatedWordsInADict(String[] words) {
if (words == null || words.length == 0) {return Collections.EMPTY_LIST;}
Arrays.sort(words, Comparator.comparing(String::length));
Set<String> wordSet = new HashSet<>();
List<String> result = new ArrayList<>();
for (String word : words) {
if (find(wordSet, word)){
result.add(word);
}
}
return result;
}
public boolean find(Set<String> wordSet, String word) {
if (wordSet.isEmpty()) {
wordSet.add(word);
return false;
}
boolean[] dp = new boolean[word.length()+1];
dp[0] = true;
for (int i = 1; i<=word.length() ; i++) {
for (int j = 0 ; j<i ; j++) {
if (!dp[j]){ continue;}
if (wordSet.contains(word.substring(j, i))) {
dp[i] = true;
break;
}
}
}
wordSet.add(word);
return dp[word.length()];
}
思路二:深度優先遍歷
深度優先遍歷的思想本質上和動態規劃思想是類似的,這個是網上網友的答案,還做了另一層優化,即在最開始計算出整個數組中最短的字符串長度,並且以該字符串作爲自字符串比對的起始長度。
代碼如下:
public List<String> findAllConcatenatedWordsInADict(String[] words) {
List<String> res = new LinkedList<>();
// corner case
if(words == null || words.length == 0) return res;
// 1. Create HashSet
Set<String> set = new HashSet<>();
int minLen = Integer.MAX_VALUE;
for(String word : words){
if(word.length() > 0){
set.add(word);
minLen = Math.min(minLen, word.length());
}
}
for(String word : words){
if(canCompose(word, 0, minLen, set)) res.add(word);
}
return res;
}
public boolean canCompose(String word, int start, int minLen, Set<String> set){
for(int i = start + minLen; i <= word.length() - minLen; i++){
if(set.contains(word.substring(start, i)) && (set.contains(word.substring(i)) || canCompose(word, i, minLen, set))) return true;
}
return false;
}