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

题意

  • 给你一个长为 N N N的数组 A A A,返回 A A A中满足条件的子串个数
  • 条件是:子串中恰好有 K K K个不同的元素
  • 数据范围: N ≤ 20000 N \le 20000 N20000

思路

  • 这种子串的问题一般是用two pointer或者DP来解决,这个题比较有意思的是两种思路都要用到
  • 先根据DP的想法,我们考虑子问题,设前 i i i个元素这样的子串有 F ( i ) F(i) F(i)个,必须以 i i i结尾的子串有 f ( i ) f(i) f(i)
  • 这样有一个很简单的结论: F ( i ) = F ( i − 1 ) + f ( i ) F(i) = F(i-1) + f(i) F(i)=F(i1)+f(i)
  • 那么我们的问题的关键就是用 f ( i − 1 ) f(i-1) f(i1)的知识帮助我们求出 f ( i ) f(i) f(i)了,很显然这两个部分关联性非常强,举个例子比如 A ( i ) = A ( i − 1 ) A(i) = A(i-1) A(i)=A(i1)的情况,那么有 f ( i ) = f ( i − 1 ) f(i) = f(i-1) f(i)=f(i1)
  • 这里我们需要一些其它的辅助信息了,首先是两个指针 p 1 ( i ) p_1(i) p1(i) p 2 ( i ) p_2(i) p2(i),我们让他们表示以 i i i结尾的可行子串的第一个和最后一个开头位置的索引,不难发现,如果存在以 i i i结尾的可行子串,那么所有的 i i i结尾可行子串的开头位置应该都在 p 1 ( i ) p_1(i) p1(i) p 2 ( i ) p_2(i) p2(i)之间,而且它们之间的位置作为头也都是可行的,即 f ( i ) = p 2 ( i ) − p 1 ( i ) + 1 f(i) = p_2(i) - p_1(i) + 1 f(i)=p2(i)p1(i)+1
  • 另外,我们还需要记录一下映射map记为 m i m_i mi,他记录以 i i i结尾的子串里,包含的数值,以及数值对应出现的位置,要求这个位置是在 i i i之前该数值最后一次出现的位置
  • 那么我们接下来就是用 p 1 ( i ) p_1(i) p1(i) p 2 ( i ) p_2(i) p2(i) m i m_i mi来更新 p 1 ( i + 1 ) p_1(i+1) p1(i+1) p 2 ( i + 1 ) p_2(i+1) p2(i+1) m i + 1 m_{i+1} mi+1
  • 加入 A ( i + 1 ) A(i+1) A(i+1)出现在了 m i m_i mi中,那么其实 p 1 ( i ) p_1(i) p1(i) p 2 ( i ) p_2(i) p2(i)之间的位置做开头一定都可行,只是如果 p 2 ( i ) = A ( i + 1 ) p_2(i) = A(i+1) p2(i)=A(i+1)时,我们会有更多可行解;我们首先更新 m ( A ( i + 1 ) ) = i + 1 m(A(i+1)) = i+1 m(A(i+1))=i+1并赋值给 m i + 1 m_{i+1} mi+1,需要通过遍历更新 p 2 p_2 p2,我们找到第一个位置 j j j,保证 A ( j ) = = m i + 1 ( A ( j ) ) A(j) == m_{i+1}(A(j)) A(j)==mi+1(A(j)),领 p 2 ( i + 1 ) = j p_2(i+1)=j p2(i+1)=j
  • 如果 A ( i + 1 ) A(i+1) A(i+1)未出现在 m i m_i mi中,那么 p 1 ( i ) p_1(i) p1(i) p 2 ( i ) p_2(i) p2(i)之间的位置做开头就会多一个元素,不难发现这个元素就是 A ( p 2 ( i ) ) A(p_2(i)) A(p2(i)),因此我们令 p 1 ( i + 1 ) = p 2 ( i ) + 1 p_1(i+1) = p_2(i) + 1 p1(i+1)=p2(i)+1 p 1 ( i + 1 ) p_1(i+1) p1(i+1)的更新则和上一个条件分支基本一样,然后更新 m i m_i mi即可
  • 我们可以发现 p 1 p_1 p1 p 2 p_2 p2的更新都是单调递增的,因此每个位置至多被遍历一次,所以总体复杂度是 O ( N ) 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;
    }
};

你可能感兴趣的:(ACM_DP)