题意
- 给你一个长为 N N N的数组 A A A,返回 A A A中满足条件的子串个数
- 条件是:子串中恰好有 K K K个不同的元素
- 数据范围: N ≤ 20000 N \le 20000 N≤20000
思路
- 这种子串的问题一般是用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(i−1)+f(i)
- 那么我们的问题的关键就是用 f ( i − 1 ) f(i-1) f(i−1)的知识帮助我们求出 f ( i ) f(i) f(i)了,很显然这两个部分关联性非常强,举个例子比如 A ( i ) = A ( i − 1 ) A(i) = A(i-1) A(i)=A(i−1)的情况,那么有 f ( i ) = f ( i − 1 ) f(i) = f(i-1) f(i)=f(i−1)
- 这里我们需要一些其它的辅助信息了,首先是两个指针 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;
}
};