算法从入门到放弃——第三期 单词拆分

leetcode上很多单词拆分的题,汇总一下看看

573

给你一个字符串 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"。
     注意你可以重复使用字典中的单词。 

先思考:这种题我们第一感觉就是你想知道拆分完一个以后的字符串能否能继续拆,也就是说这一时刻的状态依赖上一时刻的状态,大概率先走动态规划

当然你熟悉背包的话,这就是完全背包问题(物品给定,数量不限,选一个或多个构成特定情况,典型的完全背包)

第一反应dp[i]是前i个字符串拆分之后是否在数组里,写写看,往前找到一样条件环境的闭环,这就是动规的思路。

dp[i] = dp[i-1]。。。传统递归是找和上一位的,但是这里字符串是由单词组成的,所以要找截取过一个单词的上一个,那可能还要引入单词长度,总结一下,dp[i]和截取一个长度为k的单词之后,dq[i-k]之间的关系,dq[i-k]可以和上一个dq[i-k-k]之间的关系,由此动规代码基本出来了

我这里没有加任何优化,只做AC,为了工作。。。。,注意这个方式和官网的解法不太一样,很多边界条件和过程是debug的时候加的,你必须大脑里有程序运行的过程,这样能更好的帮你debug,加边界

    public static boolean wordBreak(String s, List<String> wordDict) {
        boolean[] dp = new boolean[s.length()];
        for (int i = 0; i < s.length(); i++) {
            for (int k = 0; k <= i; k++) {
                //如果是前k个字符组成的单词,看看是不是在dict里
                if (k == i) {
                    String substring = s.substring(0, i + 1);
                    dp[i] = wordDict.contains(substring);
                    if (dp[i]) {
                        continue;
                    }
                }
                //如果不是前k个,那么减去k个看看i-k到i是不是在dict里,并循环这个过程
                String substring = s.substring(i - k + 1, i + 1);
                dp[i] = dp[i - k] && wordDict.contains(substring);
                if (dp[i]) {
                    break;
                }
            }
        }
        return dp[s.length() - 1];
    }

还没完,如果你觉得可以把leetcode拆分成

[l,le,lee,leet,leetc,leetco,leetcod,leetcode],之后对

[l]+[e,ee,eet,eetc,eetco,eetcod,eetcode]

[le]+[e,et,etc,etco,etcod,etcode] ....以此类推,你会发现这又是一棵树,一颗多叉树。

多叉树bfs还是dfs,这个题目应该都是可以,但明显我们目标明确,就是为了看有没有,那么优先还是dfs

需要的回去看看我之前的模板:算法从入门到放弃——第二期_u014783007的博客-CSDN博客

    public static void wordBreak3(String s, int index, List<List<String>> result, List<String> templist) {
        if (index >= s.length()) {
            result.add(new ArrayList<>(templist));
            return;
        }
        for (int i = index; i < s.length(); i++) {
            String substring = s.substring(index, i + 1);
            if (!isPalindrome(substring)) {
                continue;
            }
            String substring1 = s.substring(index, i + 1);
            templist.add(substring1);
            wordBreak3(s, index + 1, result, templist);
            templist.remove(templist.size() - 1);
        }
    }

    public static boolean isPalindrome(String string) {
        boolean result = true;
        for (int i = 0; i < string.length() / 2; i++) {
            if (!(string.charAt(i) == string.charAt(string.length() - i - 1))) {
                result = false;
                break;
            }
        }
        return result;
    }

套用模板+简单调试就能得到结果,至于为什么这里i=index而不是i=0套用,我觉的你还是要debug几次去体会一下, 算法题你的思路只有和机器一样才能一下AC,庸者如我大概率只能边debug边改。。。index标识字符串开始截取的位置,i标识第一层树我们分多少叉,大家自己体会一下,尤其是递归过程中index和i的变化,你不debug一下,你很难想想的到。

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。

说明:

分隔时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
示例 1:

输入:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
输出:
[
  "cats and dog",
  "cat sand dog"
]
示例 2:

输入:
s = "pineapplepenapple"
wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
输出:
[
  "pine apple pen apple",
  "pineapple pen apple",
  "pine applepen apple"
]
解释: 注意你可以重复使用字典中的单词。
示例 3:

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

这题和上一题明显要求都一样,不重复的dict,可重复拿的元素,一眼看去又像是完全背包,那就先拿动规试试

题目中一旦出现所有解这种字眼,很明显结果不在单一,那么动规就有局限性了,你应该快速反应的是bfs, 再简单想一点,dict几个单词就直接几叉树分到底

算了,TODO吧。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值