leetCode進階算法題+解析(五十六)

最近工作還算是清閑醉者,也不用加班什么的厚者,所以有了大把時間刷題。直接開始吧晃琳。

隨即翻轉(zhuǎn)矩形

題目:題中給出一個 n_rows 行 n_cols 列的二維矩陣,且所有值被初始化為 0。要求編寫一個 flip 函數(shù)蝎土,均勻隨機的將矩陣中的 0 變?yōu)?1视哑,并返回該值的位置下標(biāo) [row_id,col_id];同樣編寫一個 reset 函數(shù)誊涯,將所有的值都重新置為 0挡毅。盡量最少調(diào)用隨機函數(shù) Math.random(),并且優(yōu)化時間和空間復(fù)雜度暴构。
注意:
1 <= n_rows, n_cols <= 10000
0 <= row.id < n_rows 并且 0 <= col.id < n_cols
當(dāng)矩陣中沒有值為 0 時跪呈,不可以調(diào)用 flip 函數(shù)
調(diào)用 flip 和 reset 函數(shù)的次數(shù)加起來不會超過 1000 次

示例 1:
輸入:
["Solution","flip","flip","flip","flip"]
[[2,3],[],[],[],[]]
輸出: [null,[0,1],[1,2],[1,0],[1,1]]
示例 2:
輸入:
["Solution","flip","flip","reset","flip"]
[[1,2],[],[],[],[]]
輸出: [null,[0,0],[0,1],null,[0,0]]
輸入語法解釋:
輸入包含兩個列表:被調(diào)用的子程序和他們的參數(shù)。Solution 的構(gòu)造函數(shù)有兩個參數(shù)取逾,分別為 n_rows 和 n_cols耗绿。flip 和 reset 沒有參數(shù),參數(shù)總會以列表形式給出砾隅,哪怕該列表為空

思路:這個題怎么說呢误阻,我好想是沒太看懂。而且這里說最少用random晴埂。最少就一次唄究反。先用我的理解去寫寫代碼試試吧。這種題目本來就是開放的儒洛。
好了精耐,第一版本代碼出來了,我先貼代碼:

class Solution {
    Set<String> set;
    int r;
    int c;
    public Solution(int n_rows, int n_cols) {
        set = new HashSet<String>();
        r = n_rows;
        c = n_cols;
    }
    
    public int[] flip() {
        int rr = new Random().nextInt(r);
        int cc = new Random().nextInt(c);
        while(set.contains(rr+","+cc)){
            rr = new Random().nextInt(r);
            cc = new Random().nextInt(c);
        }
        set.add(rr+","+cc);
        return new int[]{rr,cc};
    }
    
    public void reset() {
        set.clear();
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(n_rows, n_cols);
 * int[] param_1 = obj.flip();
 * obj.reset();
 */

沒啥技術(shù)含量琅锻,畢竟這種開放的題目對付實現(xiàn)就完事了卦停。但是我估計肯定是有更合適的做法。我去看下題解吧恼蓬。

class Solution {

    Map<Integer, Integer> V = new HashMap<>();
    int nr, nc, rem;
    Random rand = new Random();

    public Solution(int n_rows, int n_cols) {
        nr = n_rows;
        nc = n_cols;
        rem = nr * nc;
    }

    public int[] flip() {
        int r = rand.nextInt(rem--);
        int x = V.getOrDefault(r, r);
        V.put(r, V.getOrDefault(rem, rem));
        return new int[]{x / nc, x % nc};
    }

    public void reset() {
        V.clear();
        rem = nr * nc;
    }
}

我還特意去百度代碼中的一個方法:

getOrDefault(key,  defaultValue)

意味如果有這個key惊完,則返回key對應(yīng)的value。如果沒有這個key处硬,則返回給定的defaultValue.簡單來說上面題目的思路是做Map映射小槐,如果key不存在,則取并且從尾部取值對應(yīng)到Key上郁油,如果key存在本股,取對應(yīng)的Value攀痊,并且把隨機范圍減一桐腌。這樣做到用一維的map來維護了n*c個元素的二維數(shù)組。
這個題我寫的挺無腦的苟径,答案挺精巧的案站。總而言之是個挺有意思的題棘街。學(xué)到的是一種新的思維方式蟆盐。就是map映射做跳表的思路承边。。下一題石挂。

最長特殊序列2

題目:給定字符串列表博助,你需要從它們中找出最長的特殊序列。最長特殊序列定義如下:該序列為某字符串獨有的最長子序列(即不能是其他字符串的子序列)痹愚。子序列可以通過刪去字符串中的某些字符實現(xiàn)富岳,但不能改變剩余字符的相對順序≌空序列為所有字符串的子序列窖式,任何字符串為其自身的子序列。輸入將是一個字符串列表动壤,輸出是最長特殊序列的長度。如果最長特殊序列不存在,返回 -1 疑苫。

示例:
輸入: "aba", "cdc", "eae"
輸出: 3
提示:
所有給定的字符串長度不會超過 10 肮帐。
給定字符串列表的長度將在 [2, 50 ] 之間。

思路:這個題怎么說呢肩碟,本身序列的題就比較復(fù)雜强窖。畢竟不是字面量,可以跳元素削祈。你就看著限制條件翅溺。字符串長度不會超過十∷枰郑總數(shù)小于50就能看出計算有多復(fù)雜咙崎。反正這種題目第一反應(yīng)是dp沒啥問題。至于狀態(tài)轉(zhuǎn)移方程我的慢慢去琢磨吨拍。大概的思路是每一個和其余的比找出特殊的序列褪猛。然后這些特殊的繼續(xù)往下比,應(yīng)該是個二維數(shù)組羹饰。我去試試吧伊滋。
算了,dp個屁队秩,我還是先用自己的方式對付實現(xiàn)了再說吧笑旺。就暴力破解了。
直接貼代碼:

class Solution {
    public int findLUSlength(String[] strs) {
        Arrays.sort(strs, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length()>o2.length()?-1:1;
            }
        });
        if(strs[0].length()>strs[1].length()) return strs[0].length();
        for(int i = 0;i<strs.length;i++){
            String res = strs[i];
            for(int j = 0;j<strs.length;j++){
                if(i==j) continue;
                res = getDiff(res, strs[j]);
                //說明這個元素沒有自己的子序列
                if("".equals(res)) break;
            }
            if(!"".equals(res)) return res.length();
        }
        return -1;
    }
    public String getDiff(String a,String b) {
        char[] aa = a.toCharArray();
        int idx = 0;
        for(char c:b.toCharArray()) {
            //這個元素有了繼續(xù)往下對比
            if(c==aa[idx]) {
                //如果最后一個元素都比對完了說明這個序列不是唯一的馍资。返回空
                if(idx==aa.length-1) return "";
                idx++;
            }
        }
        //到這還沒返回說明這個子序列 b中沒有筒主,暫時算是特殊的
        return a;
    }
}

我簡直呵呵了,性能百分百過的。我為什么會覺得這道題要dp乌妙?怕不是腦子進了水了使兔。生生想了半個多小時到懷疑人生。
這個題簡單的很藤韵,思路主要是:一個串中有特殊序列虐沥,那么這整個串一定是特殊的。有點心累泽艘,不想多說話置蜀,下一題吧。

連續(xù)的子序組合

題目:給定一個包含 非負(fù)數(shù) 的數(shù)組和一個目標(biāo) 整數(shù) k悉盆,編寫一個函數(shù)來判斷該數(shù)組是否含有連續(xù)的子數(shù)組盯荤,其大小至少為 2,且總和為 k 的倍數(shù)焕盟,即總和為 n*k秋秤,其中 n 也是一個整數(shù)。

示例 1:
輸入:[23,2,4,6,7], k = 6
輸出:True
解釋:[2,4] 是一個大小為 2 的子數(shù)組脚翘,并且和為 6灼卢。
示例 2:
輸入:[23,2,6,4,7], k = 6
輸出:True
解釋:[23,2,6,4,7]是大小為 5 的子數(shù)組,并且和為 42来农。
說明:
數(shù)組的長度不會超過 10,000 鞋真。
你可以認(rèn)為所有數(shù)字總和在 32 位有符號整數(shù)范圍內(nèi)。

思路:這個題感覺最暴力的就是每一個數(shù)開始往后加沃于。但是感覺暴力法會有問題涩咖。但是我要去試試。
第一版本暴力法解出來了繁莹,我先貼代碼:

class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        if(k==0) {
            for(int i=0;i<nums.length-1;i++){
                if(nums[i] == nums[i+1] && nums[i] == 0) return true;
            }
            return false;
        }
        for(int i = 0;i<nums.length;i++) {
            int sum = nums[i];
            for(int j = i+1;j<nums.length;j++) {
                sum += nums[j];
                if(sum%k == 0) return true;
            }
        }
        return false;
    }
}

怎么說呢檩互,現(xiàn)在越來越懶得寫題解了,感覺有的題目比較簡單咨演,沒啥好說的闸昨,太難的又說不明白。反正這道題面向測試案例編程薄风。給出的結(jié)果中0,0 k=0的話也是true饵较。所以有了第一個key=0的特殊處理。性能超過百分之五十遭赂。估計是有更好的做法循诉。但是我沒精力想了,直接去看性能排行第一的代碼:

class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, -1);
        int sum = 0, n = nums.length;
        for (int i = 0; i < n; i++) {
            sum += nums[i];
            int v = k != 0 ? sum % k : sum == 0 ? 0 : sum;
            Integer o = map.put(v, i);
            if (o != null) {
                if (i - o > 1) return true;
                map.put(v, o);
            }
        }
        return false;
    }
}

這個題的題解我覺得超級贊的一個思路嵌牺。但是人家官方的說法太不好理解了打洼。我自己的想法就是:

sum%k = n.   (sum+(x+y+z+...))%k =n。

我是不是可以理解x+y+z+...本身被k整除了逆粹?只要確定這個x.y,z..的數(shù)量大于2募疮,就說明出現(xiàn)了能大于2的k的倍數(shù)(這個題坑的一點是哪怕1,-1或者0,0這種也算正確答案僻弹。)反正數(shù)學(xué)我也就這樣了阿浓,什么取模對數(shù)啥的也心有余力不足了。用自己的方式能理解就挺好了蹋绽。下一題芭毙。

通過刪除字母匹配到字典里最長單詞

題目:給定一個字符串和一個字符串字典,找到字典里面最長的字符串卸耘,該字符串可以通過刪除給定字符串的某些字符來得到退敦。如果答案不止一個,返回長度最長且字典順序最小的字符串蚣抗。如果答案不存在侈百,則返回空字符串。

示例 1:
輸入:
s = "abpcplea", d = ["ale","apple","monkey","plea"]
輸出:
"apple"
示例 2:
輸入:
s = "abpcplea", d = ["a","b","c"]
輸出:
"a"
說明:
所有輸入的字符串只包含小寫字母翰铡。
字典的大小不會超過 1000钝域。
所有輸入的字符串長度不會超過 1000。

思路:第一反應(yīng)就是判斷這個單詞是不是給定字符串的子序列锭魔。其實也就是暴力遍歷法例证。找到最長的那個子序列。相同長度取排位靠前的迷捧。就是這樣织咧,我去試試能不能過。
翻車的地方莫名其妙漠秋。字典順序最小的字符串烦感。我本來以為是在數(shù)組中排名靠前的呢。我去把這一塊改了
貼代碼:

class Solution {
    public String findLongestWord(String s, List<String> d) {
        String res = "";
        int max = 0;
        for(String dd:d) {
            if(isOk(s, dd)) {
                //是子序列,直接取最大的
                if(max<dd.length()) {
                    max = dd.length();
                    res = dd;
                }else if(max == dd.length()) {//相同長度取字典順序最小的
                    res = dd.compareTo(res)<0?dd:res;
                }
            }
        }
        return res;
    }
    //判斷是不是子序列
    public boolean isOk(String s,String d) {
        char[] c = d.toCharArray();
        int idx = 0;
        for(char ss : s.toCharArray()) {
            if(ss==c[idx]) {
                idx++;
                //d中所有元素s中都有膛堤,所以直接返回true
                if(idx == c.length) return true;
            }
        }
        return false;
    }
}

這個沒什么好說的手趣。就是暴力破解。用到了兩個方法:一個是判斷一個字符串是不是另一個字符串的子序列肥荔。還有就是字符串字典順序比較绿渣。我這里直接調(diào)用的內(nèi)置函數(shù)compareTo。想自己寫就拆開比較就ok了燕耿,也沒啥難度就是比較墨跡中符、這個題就這樣了,下一題誉帅。

連續(xù)數(shù)組

題目:給定一個二進制數(shù)組, 找到含有相同數(shù)量的 0 和 1 的最長連續(xù)子數(shù)組(的長度)淀散。

示例 1:
輸入: [0,1]
輸出: 2
說明: [0, 1] 是具有相同數(shù)量0和1的最長連續(xù)子數(shù)組右莱。
示例 2:
輸入: [0,1,0]
輸出: 2
說明: [0, 1] (或 [1, 0]) 是具有相同數(shù)量0和1的最長連續(xù)子數(shù)組。
注意: 給定的二進制數(shù)組的長度不會超過50000档插。

思路:不知道為啥這個題沒有任何時間空間的約束慢蜓。反正暴力法就是數(shù)組最長才5w。我的想法就是建立一個list(因為不定長所以不用數(shù)組了)郭膛。然后0的出現(xiàn)個數(shù)統(tǒng)計晨抡,1的出現(xiàn)個數(shù)統(tǒng)計。就這樣往下統(tǒng)計下去则剃。最后遍歷list找到連續(xù)兩個數(shù)的較小的值中的最大值耘柱。說起來比較繞,但是我覺得實現(xiàn)起來還是很容易的棍现。我去試試了调煎。
好吧,是我沒理解題意己肮。這里0,1汛蝙,0,1我以為結(jié)果是2.但是沒想到這樣是4朴肺,所以上面的思路推翻重試窖剑。暴力法就不合適了。我想想怎么實現(xiàn)吧戈稿。
我覺得我思路賊精巧西土,還好這兩道題挨在一起了所以找到了思路。但是性能不咋地鞍盗。我先附上代碼:

class Solution {
    public int findMaxLength(int[] nums) {
        for(int i = 0;i<nums.length;i++){
            if(nums[i]==0) nums[i]=-1;
        }
        //找到和為0的最長子序列(和上一道題類似)
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        map.put(0,-1);//默認(rèn)就是0了需了。
        int sum = 0;
        int max = 0;
        for(int i = 0;i<nums.length;i++){
            sum += nums[i];
            //當(dāng)再次出現(xiàn)和是sum,說明中間累加這么多和為0.也就是1和-1數(shù)目相等
            if(map.containsKey(sum)) {
                max = Math.max(max, i-map.get(sum));
            }else {
                map.put(sum,i);//第一次出現(xiàn)和是sum的情況般甲。
            }
        }
        return max;
    }

上一道題也是這個思路肋乍,我覺得我注釋寫的比較清楚了。真的這兩道題安排在一起幫了我敷存。我再去看看性能排行第一的代碼:

class Solution {
    public int findMaxLength(int[] nums) {
        if (null == nums || nums.length < 2) {
            return 0;
        }

        int length = nums.length;
        int mappingLength = 2 * length + 1;
        int[] mapping = new int[mappingLength];
        for (int i = 0; i < mappingLength; i++) {
            mapping[i] = -2;
        }
        mapping[length] = -1;

        int maxLength = 0;
        int count = 0;
        for (int i = 0; i < length; i++) {
            count += 2 * nums[i] - 1;
            if (mapping[count + length] >= -1) {
                maxLength = Math.max(maxLength, i - mapping[count + length]);
            } else {
                mapping[count + length] = i;
            }
        }
        return maxLength;
    }
}

本質(zhì)上和我的思路是一樣的墓造。不過我把0賦值為-1了。而這里用的是2 * nums[i] - 1锚烦、算出來還是1就+1.0就-1觅闽。
區(qū)別就是我用的map存儲。而這里用數(shù)組存儲了涮俄。其實我覺得就是用空間換時間嘛蛉拙。不是很驚喜。這道題就這樣了彻亲。
這篇筆記就記到這里孕锄,如果稍微幫到你了記得點個喜歡點個關(guān)注吮廉。也祝大家工作順順利利!生活愉快畸肆!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市恼除,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌曼氛,老刑警劉巖豁辉,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異舀患,居然都是意外死亡徽级,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門聊浅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來餐抢,“玉大人,你說我怎么就攤上這事低匙】鹾郏” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵顽冶,是天一觀的道長欺抗。 經(jīng)常有香客問我,道長强重,這世上最難降的妖魔是什么绞呈? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮间景,結(jié)果婚禮上佃声,老公的妹妹穿的比我還像新娘。我一直安慰自己倘要,他們只是感情好圾亏,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著封拧,像睡著了一般召嘶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哮缺,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天弄跌,我揣著相機與錄音,去河邊找鬼尝苇。 笑死铛只,一個胖子當(dāng)著我的面吹牛埠胖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播淳玩,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼直撤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蜕着?” 一聲冷哼從身側(cè)響起谋竖,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎承匣,沒想到半個月后蓖乘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡韧骗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年嘉抒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袍暴。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡些侍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出政模,到底是詐尸還是另有隱情岗宣,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布淋样,位于F島的核電站狈定,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏习蓬。R本人自食惡果不足惜纽什,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望躲叼。 院中可真熱鬧芦缰,春花似錦、人聲如沸枫慷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽或听。三九已至探孝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間誉裆,已是汗流浹背顿颅。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留足丢,地道東北人粱腻。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓庇配,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绍些。 傳聞我的和親對象是個殘疾皇子捞慌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355