博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[LeetCode] Word Break II 拆分词句之二
阅读量:5782 次
发布时间:2019-06-18

本文共 3338 字,大约阅读时间需要 11 分钟。

 

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.

Note:

  • The same word in the dictionary may be reused multiple times in the segmentation.
  • You may assume the dictionary does not contain duplicate words.

Example 1:

Input:s = "catsanddog"wordDict = ["cat", "cats", "and", "sand", "dog"]Output:[  "cats and dog",  "cat sand dog"]

Example 2:

Input:s = "pineapplepenapple"wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]Output:[  "pine apple pen apple",  "pineapple pen apple",  "pine applepen apple"]Explanation: Note that you are allowed to reuse a dictionary word.

Example 3:

Input:s = "catsandog"wordDict = ["cats", "dog", "sand", "and", "cat"]Output:[]

 

这道题是之前那道的拓展,那道题只让我们判断给定的字符串能否被拆分成字典中的词,而这道题加大了难度,让我们求出所有可以拆分成的情况,就像题目中给的例子所示。之前的版本中字典wordDict的数据类型是HashSet,现在的不知为何改成了数组vector,而且博主看到第二个例子就笑了,PPAP么,哈哈。

根据博主行走江湖多年的经验,像这种返回结果要列举所有情况的题,十有八九都是要用递归来做的。当我们一时半会没有啥思路的时候,先不要考虑代码如何实现,如果就给你一个s和wordDict,不看Output的内容,你会怎么找出结果。比如对于例子1,博主可能会先扫一遍wordDict数组,看有没有单词可以当s的开头,那么我们可以发现cat和cats都可以,比如我们先选了cat,那么此时s就变成了 "sanddog",我们再在数组里找单词,发现了sand可以,最后剩一个dog,也在数组中,于是一个结果就出来了。然后回到开头选cats的话,那么此时s就变成了 "anddog",我们再在数组里找单词,发现了and可以,最后剩一个dog,也在数组中,于是另一个结果也就出来了。那么这个查询的方法很适合用递归来实现,因为s改变后,查询的机制并不变,很适合调用递归函数。再者,我们要明确的是,如果不用记忆数组做减少重复计算的优化,那么递归方法跟brute force没什么区别,大概率无法通过OJ。所以我们要避免重复计算,如何避免呢,还是看上面的分析,如果当s变成 "sanddog"的时候,那么此时我们知道其可以拆分成sand和dog,当某个时候如果我们又遇到了这个 "sanddog"的时候,我们难道还需要再调用递归算一遍吗,当然不希望啦,所以我们要将这个中间结果保存起来,由于我们必须要同时保存s和其所有的拆分的字符串,那么可以使用一个HashMap,来建立二者之间的映射,那么在递归函数中,我们首先检测当前s是否已经有映射,有的话直接返回即可,如果s为空了,我们如何处理呢,题目中说了给定的s不会为空,但是我们递归函数处理时s是会变空的,这时候我们是直接返回空集吗,这里有个小trick,我们其实放一个空字符串返回,为啥要这么做呢?我们观察题目中的Output,发现单词之间是有空格,而最后一个单词后面没有空格,所以这个空字符串就起到了标记当前单词是最后一个,那么我们就不要再加空格了。接着往下看,我们遍历wordDict数组,如果某个单词是s字符串中的开头单词的话,我们对后面部分调用递归函数,将结果保存到rem中,然后遍历里面的所有字符串,和当前的单词拼接起来,这里就用到了我们前面说的trick。for循环结束后,记得返回结果res之前建立其和s之间的映射,方便下次使用,参见代码如下:

 

解法一:

class Solution {public:    vector
wordBreak(string s, vector
& wordDict) { unordered_map
> m; return helper(s, wordDict, m); } vector
helper(string s, vector
& wordDict, unordered_map
>& m) { if (m.count(s)) return m[s]; if (s.empty()) return { ""}; vector
res; for (string word : wordDict) { if (s.substr(0, word.size()) != word) continue; vector
rem = helper(s.substr(word.size()), wordDict, m); for (string str : rem) { res.push_back(word + (str.empty() ? "" : " ") + str); } } return m[s] = res; }};

 

我们也可以将将主函数本身当作递归函数,这样就不用单独的使用一个递归函数了,不过我们的HashMap必须是全局了,写在外部就好了,参见代码如下:

 

解法二:

class Solution {public:    unordered_map
> m; vector
wordBreak(string s, vector
& wordDict) { if (m.count(s)) return m[s]; if (s.empty()) return {
""}; vector
res; for (string word : wordDict) { if (s.substr(0, word.size()) != word) continue; vector
rem = wordBreak(s.substr(word.size()), wordDict); for (string str : rem) { res.push_back(word + (str.empty() ? "" : " ") + str); } } return m[s] = res; }};

 

类似题目:

 

参考资料:

 

转载地址:http://jpcyx.baihongyu.com/

你可能感兴趣的文章
解决使用react-image-lightbox组件,关闭后元素自动滑动问题(tabindex)
查看>>
理解 GNU Libtool
查看>>
iOS设备中的推送(四):本地推送(闹钟)
查看>>
LDAP实施实战
查看>>
如何通过solc编译solidity编写的以太坊智能合约
查看>>
限时 | 你需要的人工智能学习资料,都在这里【附下载】
查看>>
一款简单的UILabel,可设置字间距,行间距等
查看>>
[Vue]手撸多文件上传组件
查看>>
自学 JAVA 的几点建议
查看>>
五个案例,三大心得,Meratix 创始人带你进阶深度学习的实践应用之路
查看>>
MaxCompute安全管理指南-基础篇
查看>>
2019年快来了,送给每位程序员一份新年计划清单
查看>>
Java Web开发中文乱码问题
查看>>
前端如何优化代码?
查看>>
Spring boot学习(六)Spring boot实现AOP记录操作日志
查看>>
@程序员,你会教自己的孩子学习编程吗?
查看>>
轻松学会责任链模式
查看>>
威胁快报|ProtonMiner挖矿蠕虫扩大攻击面,加速传播
查看>>
<<深入PHP面向对象、模式与实践>>读书笔记:面向对象设计和过程式编程
查看>>
架构的“一小步”,业务的一大步
查看>>