Leetcode 139: 单词切分

Leetcode 139: 单词切分

题目描述

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:

拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。

示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
    注意你可以重复使用字典中的单词。

示例 3:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

思路

思路1:暴力遍历(时间爆表)

检查每一个前缀的子字符串回溯实现

思路2:记忆化可切分后缀

记录一个回溯记忆数组,若该后缀可切分则综合到最后可切分,这样可以大幅减少调用函数次数

思路3: BFS实现

使用广度优先遍历实现,相当于将可到达相同的前缀节点合并,不进行相同的后续操作,减少函数调用次数。

思路4: DP实现

第j个位置可根据可形成前缀的位置进行计算,若其前缀为i,则若s.sub(i, j)在字典中则第j个位置为可拼接,最后一个位置即为最后是否可切分结果。

代码

代码1

public class Solution {
    public boolean wordBreak(String s, List wordDict) {
        if (s == null || s.length() == 0) {
            return true;
        } else if (wordDict.size() == 0) {
            return false;
        }
        HashSet hashSet = new HashSet<>(wordDict);
        return wordBreakHelper(s, 0, hashSet);

    }

    private boolean wordBreakHelper(String s, int startIndex, HashSet hashSet) {
        if (startIndex >= s.length()) {
            return true;
        }
        boolean hasMatch = false;
        for(var neededStr: hashSet) {
//            System.out.println(s.substring(startIndex, startIndex + neededStr.length()));
            if (startIndex + neededStr.length() <= s.length() && s.substring(startIndex, startIndex + neededStr.length()).equalsIgnoreCase(neededStr)) {
                hasMatch = hasMatch || wordBreakHelper(s, startIndex + neededStr.length(), hashSet);
                if (hasMatch) {
                    break;
                }
            }
        }
        return hasMatch;
    }
}

代码2

public class Solution2 {
    public boolean wordBreak(String s, List wordDict) {
        if (s == null || s.length() == 0) {
            return true;
        } else if (wordDict.size() == 0) {
            return false;
        }
        HashSet hashSet = new HashSet<>(wordDict);
        return wordBreakHelper(s, 0, hashSet, new Boolean[s.length()]);
    }

    private boolean wordBreakHelper(String neededStr, int startIndex, HashSet hashSet, Boolean[] hasMatch) {
        if (startIndex == neededStr.length()) {
            return true;
        }
        if (hasMatch[startIndex] != null) {
            return hasMatch[startIndex];
        }
        for(int i=startIndex + 1; i <= neededStr.length(); i++) {
            if (hashSet.contains(neededStr.substring(startIndex, i)) && wordBreakHelper(neededStr, i, hashSet, hasMatch)) {
                return hasMatch[startIndex] = true;
            }
        }
        return hasMatch[startIndex] = false;
    }
}

代码3:BFS (队列实现)

public class Solution3 {
    public boolean wordBreak(String s, List wordDict) {
        if (s == null || s.length() == 0) {
            return true;
        } else if(wordDict.size() == 0) {
            return false;
        }
        Set hashSet = new HashSet<>(wordDict);
        Queue queue = new LinkedList<>();
        boolean[] hasSeen = new boolean[s.length()];
        queue.add(0);
        while (!queue.isEmpty()) {
            int startIndex = queue.remove();
            if (!hasSeen[startIndex]) {
                for(int endIndex = startIndex + 1; endIndex <= s.length(); endIndex++) {
                    if (hashSet.contains(s.substring(startIndex, endIndex))) {
                        queue.add(endIndex);
                        if (endIndex == s.length()) {
                            return true;
                        }
                    }
                }
                hasSeen[startIndex] = true;
            }
        }
        return false;
    }
}

代码4: DP实现

public class Solution4 {
    public boolean wordBreak(String s, List wordDict) {
        if (s == null || s.length() == 0) {
            return true;
        } else if(wordDict.size() == 0) {
            return false;
        }
        Set hashSet = new HashSet<>(wordDict);
        boolean[] dps = new boolean[s.length() + 1];
        dps[0] = true;

        for(int end = 1; end < s.length() + 1; end++) {
            for(int start=0; start < end; start++) {
                if (dps[start] && hashSet.contains(s.substring(start, end))) {
                    dps[end] = true;
                    break;
                }
            }
        }
        return dps[s.length()];
    }
}

复杂度分析

思路1:时间复杂度

$n^n$

思路1: 空间复杂度

$n$

思路2: 时间复杂度

$n^2$

思路2:空间复杂度

$n$

思路3: 时间复杂度

$n^2$

思路3:空间复杂度

$n$

思路4: 时间复杂度

$n^2$

思路4:空间复杂度

$n$

总结

重点在于如何将重复或者非必需的步骤省略


文章作者: 小风雷
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小风雷 !
评论
 上一篇
Leetcode 140: 单词切分II Leetcode 140: 单词切分II
Leetcode 140: 单词切分II题目描述给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。 说明:分隔时可以重复使用字
2020-03-19
下一篇 
Leetcode 131: 分割回文串 Leetcode 131: 分割回文串
Leetcode 131: 分割回文串题目描述给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。返回 s 所有可能的分割方案。 示例输入: "aab" 输出: [ ["aa",
2020-03-17
  目录