给你一个 不含重复 单词的字符串数组 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


    1. class Solution {
    2. /**
    3. 可以用字符串哈希预处理字符串,这样我们set中存储的就是数字,然后用序列dp
    4. 记录s是否是连接词,f[i]表示考虑s中前i个数,分割单词的数量
    5. 状态转移方程为 当在set中找到当前hash时
    6. f[j] = max(f[j],f[i+1])
    7. 判断f[n] > 1即可加入答案
    8. */
    9. Set<Long> set = new HashSet<>();
    10. int P = 131, OFFSET = 128;
    11. public List<String> findAllConcatenatedWordsInADict(String[] words) {
    12. //字符串哈希
    13. for (String s : words) {
    14. int n = s.length();
    15. long hash = 0;
    16. for (int i = 0; i < n; ++i)
    17. hash = hash * P + s.charAt(i) - 'a'+OFFSET;
    18. set.add(hash);
    19. }
    20. List<String> res = new ArrayList<>();
    21. //序列dp
    22. for (String s : words) {
    23. if (check(s)) res.add(s);
    24. }
    25. return res;
    26. }
    27. public boolean check(String s) {
    28. int n = s.length();
    29. //f[i] 表示考虑s的前i个数,所能切分的最大单词数
    30. int[] f = new int[n+1];
    31. //初始化成-1,方便剪枝
    32. Arrays.fill(f,-1);
    33. //f[0]为一种方案
    34. f[0] = 0;
    35. for (int i = 0; i <= n; ++i) {
    36. //剪枝
    37. if (f[i] == -1) continue;
    38. long hash = 0;
    39. for (int j = i + 1; j <= n; ++j) {
    40. hash = hash * P + s.charAt(j-1)-'a'+OFFSET;
    41. if (set.contains(hash))
    42. f[j] = Math.max(f[j],f[i]+1);
    43. }
    44. if (f[n] > 1) return true;
    45. }
    46. return false;
    47. }
    48. }