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