ARTS是什么?
Algorithm:每周至少作一个leetcode的算法题;
Review:阅读并点评至少一篇英文技术文章;
Tip:学习至少一个技术技巧;
Share:分享一篇有观点和思考的技术文章。java
LeetCode 472. Concatenated Words编程
题目一:思路分析
浏览器
也是一道比较经典的回溯问题,在搜索的方式上,有两种思路,第一种思路是基于字符串的字符,另一种是根据词表里面的单词,显然是第二种更优,代码也会更加简洁,可是这一题的难点在如何把暴力搜索变成记忆化搜索,也就是咱们一般说的动态规划,若是你画出递归树的话,你能够明显看到这里是有重复子问题的,这样,问题就变成了原问题和子问题的关系,以及子问题的解如何表示?由于考虑到输入字符串或者说输入字符串的子串是容许多个拆分可能性的,所以这里咱们考虑用一个 Map 去存储当前子串对应的全部可能的拆分状况,而后当前考虑的单词直接加到这些子串以前,就是当前的解,利用递归回溯更新,最后返回的就是答案,可是这里有一点须要注意的是,每当传入的字符串是空的时候,表示以前的单词是最后一个单词,这时须要在返回的 list 中添加一个空串占位,表示递归前面的函数传进来的 word 是可行的。缓存
题目一:参考代码安全
public List<String> wordBreak(String s, List<String> wordDict) {
if (s == null || s.length() == 0) {
return new ArrayList<String>();
}
Map<String, List<String>> hash = new HashMap<>();
List<String> path = helper(s, wordDict, hash);
return path;
}
private List<String> helper(String remain, List<String> wordDict, Map<String, List<String>> hash) {
if (remain.equals("")) {
List<String> l = new ArrayList<String>();
l.add("");
return l;
}
if (hash.containsKey(remain)) {
return hash.get(remain);
}
List<String> path = new ArrayList<>();
for (String word : wordDict) {
boolean isStartsWith = remain.startsWith(word);
if (isStartsWith) {
List<String> subPath = helper(remain.substring(word.length()), wordDict, hash);
for (String sub : subPath) {
if (sub.equals("")) {
path.add(word);
} else {
path.add(word + " " + sub);
}
}
}
}
hash.put(remain, path);
return path;
}
复制代码
题目二:思路分析
服务器
这道题和前面那道题很是的相似,区别就是输入参数上,这里给的就是一个词表,字符串和词表合二为一了,须要查找的字符串在词表里面。彻底能够按照上面那道题的解法,就是以记忆化搜索的形式去作动态规划,可是这一题想一想其实没有必要像以前那样使用很是多的字符串操做,字符串自己是不可变的,过多的字符串操做会对时间效率产生影响,使用 StringBuilder 这样的函数又会使代码复杂,题目要求咱们仅仅是找出符合的单词,因而这里考虑使用字典树,首先根据单词列表构建字典树这里就不用说了,这里惟一须要注意的一点是,须要考虑词表中出现空串的状况。而后后面就是每一个单词去字典树中走一遍,找到了符合要求的单词就让字典树从头开始,count + 1,可是注意传入参数和递归出口的关系,也就是字典树节点和当前字符的关系,若是像个人代码同样,字典树节点和当前字符滞后一位,那么函数出口应该考虑,若是 index 出了单词,就考虑当前节点是否是一个有效的节点,若是有效,而且 count >= 1,那么这个单词就是符合要求的。cookie
题目二:参考代码框架
private class TrieNode {
TrieNode[] children = new TrieNode[26];
boolean isWord = false;
}
private void buildTree(String[] words) {
TrieNode pointer = root;
for (String word : words) {
if (word.equals("")) {
continue;
}
pointer = root;
for (char c : word.toCharArray()) {
if (pointer.children[c - 'a'] == null) {
pointer.children[c - 'a'] = new TrieNode();
}
pointer = pointer.children[c - 'a'];
}
pointer.isWord = true;
}
}
private TrieNode root = new TrieNode();
public List<String> findAllConcatenatedWordsInADict(String[] words) {
if (words == null || words.length == 0) {
return new ArrayList<String>();
}
buildTree(words);
List<String> results = new ArrayList<>();
for (int i = 0; i < words.length; ++i) {
if (helper(root, words[i].toCharArray(), 0, 0)) {
results.add(words[i]);
}
}
return results;
}
private boolean helper(TrieNode cur, char[] word, int count, int index) {
if (cur == null) {
return false;
}
if (index == word.length) {
if (count >= 1 && cur.isWord) {
return true;
} else {
return false;
}
}
if (cur.isWord
&& helper(root.children[word[index] - 'a'], word, count + 1, index + 1)) {
return true;
}
return helper(cur.children[word[index] - 'a'], word, count, index + 1);
}
复制代码
一篇关于学习编程的建议性文章:
dom
The main pillars of learning programming — and why beginners should master them
TDD(Test-Driven-Developement)测试驱动开发
新手每每并不知道测试的重要性,他们可能会以为测试有时很烦人,不写测试可以大大地节省开发效率,可是测试,特别是单元测试是对代码安全的保障,防止代码在往后带来严重的问题。若是在一开始保持写代码就必须有测试的意识,是对后面的学习新知识颇有帮助的,养成良好的习惯真的很重要。
基础优先
函数、变量、条件判断以及循环是全部程序的基础,必须把这些基础性的,特别是不少认知性的东西弄清楚,而且赋予足够的、全面的练习后,再去考虑一些应用层面的学习;在这里必须求稳,而不是求快。
学习使用库和框架
对于新手来讲,一开始确定是须要使用别人写的代码,可以基本理解这个库的功能和用途,使用正确的框架作正确的事情,这样可以提升新手的学习以及开发效率,否则新手就会对编程失去兴趣,以为太难,独自造轮子的事情是对于经验丰富的人来讲的
找一个好老师
有时候别人的一句话等于本身想好几天,有一个好的老师带领,确实对学习颇有帮助,也会让你开始的路好走不少
挑战和动机
继续学习,持续学习的动力是源于本身心里的动机,以及外界的挑战,这两个因素都会督促你去千方百计去提升本身的能力和认知,咱们要时刻保持上进的精神,同时咱们也要明白本身当前是否是在温馨区内,敢于走出温馨区,主动去寻找本身的下一个挑战
初识 Chrome 浏览器的 Network 面板
分红五大部分:
这里有个小技巧就是,咱们能够按住 shift 键,而后鼠标悬停在某个资源上,能够显示这个资源的上下游请求(下游请求是由上游请求发起的)
另外补充一下浏览器的加载流程:
这已是第十次 ARTS 了,此次作一个阶段性地总结吧,谈谈收获,谈谈计划,再谈谈理想。