Leetcode 992. Subarrays with K Different Integers (DP + Two pointer)

題意

  • 給你一個長爲NN的數組AA,返回AA中滿足條件的子串個數
  • 條件是:子串中恰好有KK個不同的元素
  • 數據範圍:N20000N \le 20000

思路

  • 這種子串的問題一般是用two pointer或者DP來解決,這個題比較有意思的是兩種思路都要用到
  • 先根據DP的想法,我們考慮子問題,設前ii個元素這樣的子串有F(i)F(i)個,必須以ii結尾的子串有f(i)f(i)
  • 這樣有一個很簡單的結論:F(i)=F(i1)+f(i)F(i) = F(i-1) + f(i)
  • 那麼我們的問題的關鍵就是用f(i1)f(i-1)的知識幫助我們求出f(i)f(i)了,很顯然這兩個部分關聯性非常強,舉個例子比如A(i)=A(i1)A(i) = A(i-1)的情況,那麼有f(i)=f(i1)f(i) = f(i-1)
  • 這裏我們需要一些其它的輔助信息了,首先是兩個指針p1(i)p_1(i)p2(i)p_2(i),我們讓他們表示以ii結尾的可行子串的第一個和最後一個開頭位置的索引,不難發現,如果存在以ii結尾的可行子串,那麼所有的ii結尾可行子串的開頭位置應該都在p1(i)p_1(i)p2(i)p_2(i)之間,而且它們之間的位置作爲頭也都是可行的,即f(i)=p2(i)p1(i)+1f(i) = p_2(i) - p_1(i) + 1
  • 另外,我們還需要記錄一下映射map<int, int>記爲mim_i,他記錄以ii結尾的子串裏,包含的數值,以及數值對應出現的位置,要求這個位置是在ii之前該數值最後一次出現的位置
  • 那麼我們接下來就是用p1(i)p_1(i)p2(i)p_2(i)mim_i來更新p1(i+1)p_1(i+1)p2(i+1)p_2(i+1)mi+1m_{i+1}
  • 加入A(i+1)A(i+1)出現在了mim_i中,那麼其實p1(i)p_1(i)p2(i)p_2(i)之間的位置做開頭一定都可行,只是如果p2(i)=A(i+1)p_2(i) = A(i+1)時,我們會有更多可行解;我們首先更新m(A(i+1))=i+1m(A(i+1)) = i+1並賦值給mi+1m_{i+1},需要通過遍歷更新p2p_2,我們找到第一個位置jj,保證A(j)==mi+1(A(j))A(j) == m_{i+1}(A(j)),領p2(i+1)=jp_2(i+1)=j
  • 如果A(i+1)A(i+1)未出現在mim_i中,那麼p1(i)p_1(i)p2(i)p_2(i)之間的位置做開頭就會多一個元素,不難發現這個元素就是A(p2(i))A(p_2(i)),因此我們令p1(i+1)=p2(i)+1p_1(i+1) = p_2(i) + 1p1(i+1)p_1(i+1)的更新則和上一個條件分支基本一樣,然後更新mim_i即可
  • 我們可以發現p1p_1p2p_2的更新都是單調遞增的,因此每個位置至多被遍歷一次,所以總體複雜度是O(N)O(N)
class Solution {
public:
    int subarraysWithKDistinct(vector<int>& A, int K) {
        unordered_map<int, int> mapp;
        int pre = 0, suf = 0, now = 0;
        int ans = 0;
        for (; now < A.size(); now++){
            mapp[A[now]] = now;
            if (mapp.size() == K){
                break;
            }
        }
        for (int i = now; i < A.size(); i++){
            if (mapp.find(A[i]) != mapp.end()){
                mapp[A[i]] = i;
                while (mapp[A[suf]] != suf){
                    suf++;
                }
            }
            else {
                mapp.erase(A[suf++]);
                mapp[A[i]] = i;
                pre = suf;
                while (mapp[A[suf]] != suf){
                    suf++;
                }
            }
            ans += suf - pre + 1;
        }
        return ans;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章