给你一个 不含重复 单词的字符串数组 words ,请你找出并返回 words 中的所有 连接词 。
连接词 定义为:一个完全由给定数组中的至少两个较短单词组成的字符串。
示例 1:
输入:words = [“cat”,”cats”,”catsdogcats”,”dog”,”dogcatsdog”,”hippopotamuses”,”rat”,”ratcatdogcat”]
输出:[“catsdogcats”,”dogcatsdog”,”ratcatdogcat”]
解释:”catsdogcats” 由 “cats”, “dog” 和 “cats” 组成;
“dogcatsdog” 由 “dog”, “cats” 和 “dog” 组成;
“ratcatdogcat” 由 “rat”, “cat”, “dog” 和 “cat” 组成。
示例 2:
输入:words = [“cat”,”dog”,”catdog”]
输出:[“catdog”]
提示:
1 <= words.length <= 104
0 <= words[i].length <= 1000
words[i] 仅由小写字母组成
0 <= sum(words[i].length) <= 105
class Solution {
/**
可以用字符串哈希预处理字符串,这样我们set中存储的就是数字,然后用序列dp
记录s是否是连接词,f[i]表示考虑s中前i个数,分割单词的数量
状态转移方程为 当在set中找到当前hash时
f[j] = max(f[j],f[i+1])
判断f[n] > 1即可加入答案
*/
Set<Long> set = new HashSet<>();
int P = 131, OFFSET = 128;
public List<String> findAllConcatenatedWordsInADict(String[] words) {
//字符串哈希
for (String s : words) {
int n = s.length();
long hash = 0;
for (int i = 0; i < n; ++i)
hash = hash * P + s.charAt(i) - 'a'+OFFSET;
set.add(hash);
}
List<String> res = new ArrayList<>();
//序列dp
for (String s : words) {
if (check(s)) res.add(s);
}
return res;
}
public boolean check(String s) {
int n = s.length();
//f[i] 表示考虑s的前i个数,所能切分的最大单词数
int[] f = new int[n+1];
//初始化成-1,方便剪枝
Arrays.fill(f,-1);
//f[0]为一种方案
f[0] = 0;
for (int i = 0; i <= n; ++i) {
//剪枝
if (f[i] == -1) continue;
long hash = 0;
for (int j = i + 1; j <= n; ++j) {
hash = hash * P + s.charAt(j-1)-'a'+OFFSET;
if (set.contains(hash))
f[j] = Math.max(f[j],f[i]+1);
}
if (f[n] > 1) return true;
}
return false;
}
}