算法之滑动窗口
Leetcode-3尋找最長不重復子串
題目要求必須是子串,也就是必須是連續的幾個不重復字符,而不是單純計算不重復字符有多少個(如果是,直接用set很容易就解決)
此題很容易想到用滑動窗口解決
- 復雜度O(n)
- 用unordered_set而不是用set,這里不需要排序,時間復雜度可以降低
- while循環是重點,保證將要插入的這個字符和set中沒有任何一個是重復的,每次都刪除最左邊那個(有可能不是那個重復元素)。比如abcb,當i=3,要插入第四個元素b的時候,先刪除了最左邊的a,再刪除b,最后這個窗口中剩下cb。這樣做是沒有問題的,因為最大元素個數都已經記錄下來了
- ** st.find(s[i]) != st.end() **兩者都是迭代器,當find函數找不到時,返回的是st.end(),所以這個語句指的是在set中找到了s[i],集合中是有這個元素的。
## Leetcode-76最小覆蓋子串
class Solution { public:unordered_map<char,int> ms,mt;bool check(){for(const auto&p: mt){if(ms[p.first]<p.second) return false;}return true;}string minWindow(string s, string t) {int left=0,right=-1,len=s.length()+1,ansl=-1;for(const auto&c: t)//t中出現的字符以及對應的個數存在map mt中{mt[c]++;}while(right<int(s.length())){if(mt.find(s[++right])!=mt.end())//當前元素在t中,將對應的map ms個數加1,ms的內容就是窗口的內容{ms[s[right]]++;}while(check()&&left<=right)//窗口包含了t,右指停止移動,左指右移{if(right-left+1<len){len=right-left+1;ansl=left;}if(mt.find(s[left])!=mt.end()){ms[s[left]]--;//即將移除的左邊界如果是t中的,對應的個數減一}left++;}}return ansl== -1?string():s.substr(ansl,len);} };思路就是先右指針右移,直到左右指針區間已經包含t的所有元素,然后右指針固定,左指針右移,移動到不再能包含t的時候記錄區間,右指針右移,以此類推。
一個難點在于怎么判斷s包含了t中所有元素,由于t中字母個數是可以重復的,所以必須要用數組記錄每個字符的個數,這里用map很方便。check函數實現了這個功能,但是有點暴力
為什么int(s.length()),少了和int就輸出一直是空的??
后來查資料得知string類的length()函數返回的是無符號整數,以后要用的時候強制轉化一下比較保險。用在for循環一般不會出錯,但是用作條件判斷就會出錯
c++ string類length()(size())函數返回值–無符號數
Leetcode-1004最大連續1的數量
這題是先看了題解的思路,然后自己敲的代碼,下面順一下解題思路
寫代碼出現的bug:當right有了新的值之后,沒有判斷right<int(A.size())就直接使用A[right],報了下面的錯,解決方法就是 每當right有了新的值之后,就要判斷right<int(A.size())一次,才能使用A[right]
================================================================= ==45==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6040000000fc at pc 0x00000038ef5d bp 0x7ffdecff7df0 sp 0x7ffdecff7de8 READ of size 4 at 0x6040000000fc thread T0 #3 0x7fd9c384882f (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) 0x6040000000fc is located 0 bytes to the right of 44-byte region [0x6040000000d0,0x6040000000fc) allocated by thread T0 here: #8 0x7fd9c384882f (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) Shadow bytes around the buggy address: 0x0c087fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c087fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c087fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c087fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c087fff8000: fa fa fd fd fd fd fd fd fa fa 00 00 00 00 00 06 =>0x0c087fff8010: fa fa fd fd fd fd fd fa fa fa 00 00 00 00 00[04] 0x0c087fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc ==45==ABORTING
emm,然后發現別人寫的比我簡潔多了
//天知いのり 2020.5.24 class Solution { public:int longestOnes(vector<int>& A, int K) {int left(0), right(0);int max(0);while (left <= right && right < A.size()) {while (right < A.size() && (A[right] || K != 0)) {if (!A[right]) K--;right++;}max = std::max(right - left, max);if (A[left]) left++;else left++, right++;}return max;} };作者:amachi-inori 鏈接:https://leetcode-cn.com/problems/max-consecutive-ones-iii/solution/c-1004-hua-dong-chuang-kou-chao-xiang-xi-jie-shuo-/ 來源:力扣(LeetCode) 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。Leetcode面試題 滑動窗口的最大值
這竟然是簡單題???????????
這也太難了吧,一開始覺得很簡單,寫了發現事情不對勁…然后還是沒有想出來,去看了思路。本題用了雙端隊列deque
屬實沒有用過,看了有哪些函數震驚可以這么方便,butbut,有了思路到具體實現還是有一定的距離
記一下思路
- 維護一個單調遞減的隊列,所以每次放到隊列前都要把隊列中比nums[i]來的小的刪掉,用到pop_back()
- 當滿足了窗口大小i+1-k>=0才可以將最大值也就是隊首存入答案ans數組
- 當隊首元素已經不是窗口內的數的時候要刪除隊首。
第三點就是我卡了很久的點,要怎么判斷隊首元素是不是在當前窗口內部???
想了很久不知道怎么辦,看了c++版本,才發現用隊列存數字的下標就能完美解決這個問題,用下標來判斷,當前窗口是[i+1-k,i],隊首比i±k小就要pop_front()
所以對應的判斷元素的大小的時候要記得隊列中的值是下標,要轉換成數值
然后今天看到了這題的動態規劃解答,不得不說,真的nb,為什么我的腦子和人家長的不太一樣…
先講思路,把數組劃分為一個個k大小的塊,當nums.size()%k!=0時候,最后一塊沒有填滿。
- 窗口為[i,j],當窗口正好在一個塊內的時候,用left數組存從塊的最左邊到j的最大值
- 當窗口在兩個快中間時,要用left存從當前塊的最左邊到當前位置的最大值,用right存從當前塊的最右邊到當前位置的最大值
- 當前窗口在左邊塊內的最大值是right[i],在右塊內的最大值是left[j]
- 當前窗口的最大值是max(right[i],left[j])
這題不用隊列,只用數組,竟然比之前的結果差了點,評論中的人騙我雙百
總結
- 上一篇: Objective-C 适用c数学函数
- 下一篇: android 专门设计界面,andro