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 了,此次作一個階段性地總結吧,談談收穫,談談計劃,再談談理想。