LEETCODE 刷題記錄 HARD類

42. Trapping Rain Water

原題目

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!

心得

我先記錄自己的思路,首先我定義了一個結構體rain,用來存儲每個位置的高度和是否訪問過的bool,然後我先遍歷一遍數組找到最高的作爲參照,將數組存入優先隊列中,每次彈出一個次高的元素,計算它到最高的之間的盛水量,如果計算過一次就將bool變量置爲true。

struct rain
{
    int high;
    bool use;
};
class Solution {
public:
    int trap(vector<int>& height) {
        int size = height.size();
        if(size==0){return 0;}
        int count=0;
        rain r[size];
        priority_queue<pair<int,int>> pq;
        for(int i=0;i<size;i++)
        {
            r[i].high = height[i];
            r[i].use = false;
            pair<int, int> b(height[i],i);
            pq.push(b);
        }
        pair<int, int> highest,cur;
        highest = pq.top();
        pq.pop();
        while(!pq.empty())
        {
            cur = pq.top();
            pq.pop();
            if(r[cur.second].use==true){continue;}
            r[cur.second].use = true;
            if(cur.second < highest.second)
            {
                for(int i = cur.second;i < highest.second;i++)
                {
                    if(r[i].use==true){continue;}
                    r[i].use = true;
                    count += cur.first-r[i].high;
                }
            }
            else
            {
                for(int i = cur.second;i > highest.second;i--)
                {
                    if(r[i].use==true){continue;}
                    r[i].use = true;
                    count += cur.first-r[i].high;
                }
            }
        }
        return count;
    }
};

其實該問題還有別的解決思路,也就是積累法:分別維護一個maxleft和maxright,將整個池子想象成一個大的水池,不斷擡高邊界高度即可。

int trap(int A[], int n) {
        int left=0; int right=n-1;
        int res=0;
        int maxleft=0, maxright=0;
        while(left<=right){
            if(A[left]<=A[right]){
                if(A[left]>=maxleft) maxleft=A[left];
                else res+=maxleft-A[left];
                left++;
            }
            else{
                if(A[right]>=maxright) maxright= A[right];
                else res+=maxright-A[right];
                right--;
            }
        }
        return res;
    }

128. Longest Consecutive Sequence

原問題

Given an unsorted array of integers, find the length of the longest consecutive elements sequence.

Your algorithm should run in O(n) complexity.

心得

這個問題我想的沒問題,但是寫起來的時候空間用多了,導致修改了很久。核心的思想是:維護一個哈希表unordered_map,用來記錄數組中的數和對應的最大連接長度。只需要維護最長連接數組的兩端即可。

int longestConsecutive(vector<int>& nums) {
        unordered_map<int, int> numbers;
        int length = 0;
        for (int i : nums) {
            if (numbers[i]) {continue;}
            length = max(length, numbers[i]=numbers[i+numbers[i+1]]=numbers[i-numbers[i-1]]=numbers[i+1]+numbers[i-1]+1);
        }//這個地方就是維護數組中連續的兩端的節點和長度
        return length;
    }

239. Sliding Window Maximum

原問題

Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Return the max sliding window.

心得

從討論區中學到一個新的數據結構deque,雙端隊列,支持隊列收尾的添加刪除等操作。

vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> dq;
        vector<int> ans;
        for (int i=0; i<nums.size(); i++) {
            if (!dq.empty() && dq.front() == i-k) dq.pop_front();
            while (!dq.empty() && nums[dq.back()] < nums[i])
                dq.pop_back();
            dq.push_back(i);
            if (i>=k-1) ans.push_back(nums[dq.front()]);
        }
        return ans;
    }

218. The Skyline Problem

心得

vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {

        // Step 1:
		multimap<int, int> coords;
		for (const vector<int> & building : buildings) {
			coords.emplace(building[0], building[2]);
			coords.emplace(building[1], -building[2]);
		}

        // Step 2:
		multiset<int> heights = { 0 };
		map<int, int> corners;
		for (const pair<int, int> & p : coords) {
			if (p.second > 0) {
				heights.insert(p.second);
			}
			else {
				heights.erase(heights.find(-p.second));
			}
			int cur_y = *heights.rbegin();
			corners[p.first] = cur_y;
		}

        // Step 3:
		function<bool(pair<int, int> l, pair<int, int> r)> eq2nd = [](pair<int, int> l, pair<int, int> r){ return l.second == r.second;  };
		vector<pair<int, int>> results;
		unique_copy(corners.begin(), corners.end(), back_insert_iterator<vector<pair<int, int>>>(results), eq2nd);
		return results;
	}

212. Word Search II

原問題

Given a 2D board and a list of words from the dictionary, find all words in the board.

Each word must be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.

心得

這個問題很巧,在之前的字節跳動的面試中是作爲一道“easy”題出現的(服了)。我當時的直白想法就是,無非就是對每個詞組中的詞進行一次dfs搜索,也就是說O(N3N^3)的時間複雜度。我在看了討論區之後,發現大家都用到了一個數據結構trie

Trie,又稱字典樹、單詞查找樹或鍵樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計,排序和保存大量的字符串(但不僅限於字符串),所以經常被搜索引擎系統用於文本詞頻統計。它的優點是:利用字符串的公共前綴來減少查詢時間,最大限度地減少無謂的字符串比較,查詢效率比哈希樹高。

  • 首先是trie結構的實現:
    trienode 類是trie的節點,包含的屬性是,is_end:判斷是否是葉節點;children:初始化26個字母的子節點。
    trie類實現了三個功能:getRoot():獲得根節點;addWord():將一個給定的string添加到樹中;Trie():用一個string的vector初始化一個trie樹。
class TrieNode{
public:
    bool is_end;
    vector<TrieNode*> children;
    TrieNode(){
        is_end=false;
        children=vector<TrieNode*>(26, NULL);
    }   
};

class Trie{
public:
    TrieNode* getRoot(){return root;}
    Trie(vector<string>& words){
        root=new TrieNode();
        for(int i=0; i<words.size(); ++i)
            addWord(words[i]);
    }
    void addWord(const string& word){
        TrieNode* cur=root;
        for(int i=0; i<word.size(); ++i){
            int index=word[i]-'a';
            if(cur->children[index]==NULL)   
               cur->children[index]=new TrieNode();
            cur=cur->children[index];    
        }
        cur->is_end=true;
    }
private:
    TrieNode* root;
};
  • 其次是實際的代碼實現部分:
    這個就比較簡單了,trie的應用是爲了能夠讓擁有相同前綴的string在判斷到達根節點以後同時加入到set中。而爲了避免重複的方式則是將訪問過的元素暫時標記爲‘ ’。這個相比暴力的搜索法在擁有更多相同前綴的string是,效率要高得多。
class Solution {
public:
    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        Trie* trie = new Trie(words);
        TrieNode* root=trie->getRoot();
        set<string> result_set;
        for(int x=0; x<board.size(); ++x)
            for(int y=0; y<board[0].size(); ++y)
                findWords(board, x, y, root, "", result_set);
        
        vector<string> result;
        for(auto it:result_set)    result.push_back(it);
        return result;        
    }
private:
    void findWords(vector<vector<char>>& board, int x, int y, TrieNode* root, string word, set<string>& result){
        if(x<0||x>=board.size()||y<0||y>=board[0].size() || board[x][y]==' ') return;
        
        if(root->children[board[x][y]-'a'] != NULL){
            word=word+board[x][y];
            root=root->children[board[x][y]-'a']; 
            if(root->is_end) result.insert(word);
            char c=board[x][y];
            board[x][y]=' ';
            findWords(board, x+1, y, root, word, result);
            findWords(board, x-1, y, root, word, result);
            findWords(board, x, y+1, root, word, result);
            findWords(board, x, y-1, root, word, result);
            board[x][y]=c;        
        }
    }
};

140. Word Break II

原問題

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.

Note:

The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.

心得

我之前準備採用的還是分割法,但是分割法在碰到某些比較特殊的例子時,會出現time exceed limit。在討論區中發現這樣一種方法,思路是:首先用一個hash表儲存給定的字典,構造一個hashmap用來儲存當前字符串長度和輸出字符串。構造一個內聯函數sentence。核心思想是通過動態儲存來減少重複計算的浪費。

vector<string> wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordD;
        for(auto &i:wordDict){wordD.insert(i);}
        unordered_map<int, vector<string>> memo {{s.size(), {""}}};
        function<vector<string>(int)> sentences = [&](int i) {
            if (!memo.count(i))
                for (int j=i+1; j<=s.size(); j++)
                    if (wordD.count(s.substr(i, j-i)))
                        for (string tail : sentences(j))
                            memo[i].push_back(s.substr(i, j-i) + (tail=="" ? "" : ' ' + tail));
            return memo[i];
        };
        return sentences(0);
    }

10. Regular Expression Matching

原問題

Given an input string (s) and a pattern §, implement regular expression matching with support for ‘.’ and ‘*’.

  • ‘.’ Matches any single character.
  • ‘*’ Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

心得

這個題老實說我一點思路都沒有,只能去學習一下評論區的高贊答案。別人給出了兩種解決方法:

  1. 遞歸法:分兩種情況討論,一種是當p中第二位不是‘*’時,此時我們只需要判斷s[0]和p[0]是否是相同的,以及s[1…]和p[1…]是否是相同的;另一種是p的第二位是‘*’的情況,此時除了上面的判斷之外,另外需要判斷排除*字符 之後是否匹配,代碼如下
bool isMatch(string s, string p) {
        if (p.empty())    return s.empty();
        
        if ('*' == p[1])
            // x* matches empty string or at least one character: x* -> xx*
            // *s is to ensure s is non-empty
            return (isMatch(s, p.substr(2)) || !s.empty() && (s[0] == p[0] || '.' == p[0]) && isMatch(s.substr(1), p));
        else
            return !s.empty() && (s[0] == p[0] || '.' == p[0]) && isMatch(s.substr(1), p.substr(1));
    }
  1. 另一種方法是DP法,核心思想是用一個二維數組儲存s和p給定長度是否匹配。參考視頻:https://www.youtube.com/watch?v=l3hda49XcDE&list=PLrmLmBdmIlpuE5GEMDXWf0PWbBD9Ga1lO
    代碼如下:代碼如下:
bool isMatch(string s, string p) {
        /**
         * f[i][j]: if s[0..i-1] matches p[0..j-1]
         * if p[j - 1] != '*'
         *      f[i][j] = f[i - 1][j - 1] && s[i - 1] == p[j - 1]
         * if p[j - 1] == '*', denote p[j - 2] with x
         *      f[i][j] is true iff any of the following is true
         *      1) "x*" repeats 0 time and matches empty: f[i][j - 2]
         *      2) "x*" repeats >= 1 times and matches "x*x": s[i - 1] == x && f[i - 1][j]
         * '.' matches any single character
         */
        int m = s.size(), n = p.size();
        vector<vector<bool>> f(m + 1, vector<bool>(n + 1, false));
        
        f[0][0] = true;
        for (int i = 1; i <= m; i++)
            f[i][0] = false;
        // p[0.., j - 3, j - 2, j - 1] matches empty iff p[j - 1] is '*' and p[0..j - 3] matches empty
        for (int j = 1; j <= n; j++)
            f[0][j] = j > 1 && '*' == p[j - 1] && f[0][j - 2];
        
        for (int i = 1; i <= m; i++)
            for (int j = 1; j <= n; j++)
                if (p[j - 1] != '*')
                    f[i][j] = f[i - 1][j - 1] && (s[i - 1] == p[j - 1] || '.' == p[j - 1]);
                else
                    // p[0] cannot be '*' so no need to check "j > 1" here
                    f[i][j] = f[i][j - 2] || (s[i - 1] == p[j - 2] || '.' == p[j - 2]) && f[i - 1][j];
        
        return f[m][n];
    }

76. Minimum Window Substring

原題目

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

心得

類似問題,需要一個數組記錄目標的變化情況,並將每次達到要求的子串長度和其實位置記錄下來,這個count就是精髓

string minWindow(string S, string T) {
        if (S.empty() || T.empty()){return "";}
        int count = T.size();//待處理字符數
        int require[128] = {0};//記錄T中的字符,和出現次數
        bool chSet[128] = {false};//記錄是否出現過該字符
        for (int i = 0; i < count; ++i){
            require[T[i]]++;
            chSet[T[i]] = true;
        }
        int i = -1;
        int j = 0;
        int minLen = INT_MAX;
        int minIdx = 0;
        while (i < (int)S.size() && j < (int)S.size()){
            if (count){
                i++;
                require[S[i]]--;
                if (chSet[S[i]] && require[S[i]] >= 0){count--;}
            }
            else{
                if (minLen > i - j + 1){
                    minLen = i - j + 1;
                    minIdx = j;
                }
                require[S[j]]++;
                if (chSet[S[j]] && require[S[j]] > 0){count++;}
                j++;
            }
        }
        if (minLen == INT_MAX){return "";}
        return S.substr(minIdx, minLen);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章