LeetCode 1079. 活字印刷
想看更多算法題,可以掃描上方二維碼關注我微信公眾號“數據結構和算法”,截止到目前我已經在公眾號中更新了500多道算法題,其中部分已經整理成了pdf文檔,截止到目前總共有1000多頁(并且還會不斷的增加),可以在公眾號中回復關鍵字“pdf”即可下載。
回溯算法解決
這題實際上是讓求輸入的字符可以組成多少種不同的組合。之前也講過很多種類似這樣的題
《391,回溯算法求組合問題》
《448,組合的幾種解決方式》
《451,回溯和位運算解子集》
但不同的是前面幾道題字符出現的順序是不會變的,比如第451題,他的子集有123,但不能有321。但這題不一樣,輸入AAB,他的序列中,A可以在B的前面也可以在B的后面。那么這道題使用回溯算法該怎么解,我們就以示例1為例來畫個圖看一下
前面介紹的幾個組合的回溯算法,因為結果不能有重復的(比如[1,3]和[3,1]被認為是重復的結果),所以每次選擇的時候都只能從前往后選。但這題中子集[A,B]和[B,A]被認為是兩種不同的結果,所以每次都要從頭開始選擇,因為每個字符只能被使用一次,所以如果使用之后下次就不能再使用了,這里可以使用一個數組visit來標記有沒有被使用。
但這里有個難點就是怎么過濾掉上面圖中灰色的部分(也就是重復的部分)。舉個例子,比如ABBCD,如果我們選擇了第1個B,那么剩余的字符就變成了ABCD,這個時候我們再選擇第2個B是可以的。但如果我們沒選擇第1個B,直接選擇第2個B,那么剩余的字符就是ABCD,和上面重復了。所以代碼大致是這樣的
if (i - 1 >= 0 && chars[i] == chars[i - 1] && !used[i - 1])continue;在前面講《450,什么叫回溯算法,一看就會,一寫就廢》的時候,回溯算法的模板也被多次提及
private void backtrack("原始參數") {//終止條件(遞歸必須要有終止條件)if ("終止條件") {//一些邏輯操作(可有可無,視情況而定)return;}for (int i = "for循環開始的參數"; i < "for循環結束的參數"; i++) {//一些邏輯操作(可有可無,視情況而定)//做出選擇//遞歸backtrack("新的參數");//一些邏輯操作(可有可無,視情況而定)//撤銷選擇} }最后來看下這題的最終代碼
public int numTilePossibilities(String tiles) {char[] chars = tiles.toCharArray();//先排序,目的是讓相同的字符挨著,在下面計算的時候好過濾掉重復的Arrays.sort(chars);int[] res = new int[1];backtrack(res, chars, new boolean[tiles.length()], tiles.length(), 0);return res[0]; }private void backtrack(int[] res, char[] chars, boolean[] used, int length, int index) {//如果沒有可以選擇的就返回if (index == length)return;//注意,這里的i每次都是從0開始的,不是從index開始for (int i = 0; i < length; i++) {//一個字符只能選擇一次,如果當前字符已經選擇了,就不能再選了。if (used[i])continue;//過濾掉重復的結果if (i - 1 >= 0 && chars[i] == chars[i - 1] && !used[i - 1])continue;//選擇當前字符,并把它標記為已選擇used[i] = true;res[0]++;//選擇一個字符,就多了一種結果//下一分支繼續遞歸backtrack(res, chars, used, length, index + 1);//使用完之后再把它給復原。used[i] = false;} }這里還可以換一種寫法,先統計每個字符的數量,然后再使用,代碼如下。原理都類似,但他不需要去重,因為這里根本不可能出現重復的。
public int numTilePossibilities(String tiles) {char[] chars = tiles.toCharArray();//統計每個字符的數量int[] count = new int[26];for (char c : chars)count[c - 'A']++;int[] res = new int[1];backtrack(res, count);return res[0]; }private void backtrack(int[] res, int[] arr) {//遍歷所有的字符for (int i = 0; i < 26; i++) {//如果當前字符使用完了再查找下一個if (arr[i] == 0)continue;//如果沒使用完就繼續使用,然后把這個字符的數量減1arr[i]--;//使用一個字符,子集數量就會多一個res[0]++;backtrack(res, arr);//當前字符使用完之后,把它的數量還原arr[i]++;} }總結
前面也介紹過很多關于回溯算法的題,回溯算法有個大致的模板,只要掌握這個模板,然后對于不同的題在稍加修改,基本上都能做的出來。
總結
以上是生活随笔為你收集整理的LeetCode 1079. 活字印刷的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 纯java语言做rpg游戏_【纯JAVA
- 下一篇: vue.nextTick()方法的使用详