题意
给你一个长为NN N 的数组AA A ,返回AA A 中满足条件的子串个数 条件是:子串中恰好有KK K 个不同的元素 数据范围:N≤20000N \le 20000 N ≤ 2 0 0 0 0
思路
这种子串的问题一般是用two pointer或者DP来解决,这个题比较有意思的是两种思路都要用到 先根据DP的想法,我们考虑子问题,设前ii i 个元素这样的子串有F(i)F(i) F ( i ) 个,必须以ii 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 ) 这里我们需要一些其它的辅助信息了,首先是两个指针p1(i)p_1(i) p 1 ( i ) 和p2(i)p_2(i) p 2 ( i ) ,我们让他们表示以ii i 结尾的可行子串的第一个和最后一个开头位置的索引,不难发现,如果存在以ii i 结尾的可行子串,那么所有的ii i 结尾可行子串的开头位置应该都在p1(i)p_1(i) p 1 ( i ) 和p2(i)p_2(i) p 2 ( i ) 之间,而且它们之间的位置作为头也都是可行的,即f(i)=p2(i)−p1(i)+1f(i) = p_2(i) - p_1(i) + 1 f ( i ) = p 2 ( i ) − p 1 ( i ) + 1 另外,我们还需要记录一下映射map<int, int>
记为mim_i m i ,他记录以ii i 结尾的子串里,包含的数值,以及数值对应出现的位置,要求这个位置是在ii i 之前该数值最后一次出现的位置 那么我们接下来就是用p1(i)p_1(i) p 1 ( i ) ,p2(i)p_2(i) p 2 ( i ) 和mim_i m i 来更新p1(i+1)p_1(i+1) p 1 ( i + 1 ) ,p2(i+1)p_2(i+1) p 2 ( i + 1 ) 和mi+1m_{i+1} m i + 1 加入A(i+1)A(i+1) A ( i + 1 ) 出现在了mim_i m i 中,那么其实p1(i)p_1(i) p 1 ( i ) ,p2(i)p_2(i) p 2 ( i ) 之间的位置做开头一定都可行,只是如果p2(i)=A(i+1)p_2(i) = A(i+1) p 2 ( i ) = A ( i + 1 ) 时,我们会有更多可行解;我们首先更新m(A(i+1))=i+1m(A(i+1)) = i+1 m ( A ( i + 1 ) ) = i + 1 并赋值给mi+1m_{i+1} m i + 1 ,需要通过遍历更新p2p_2 p 2 ,我们找到第一个位置jj j ,保证A(j)==mi+1(A(j))A(j) == m_{i+1}(A(j)) A ( j ) = = m i + 1 ( A ( j ) ) ,领p2(i+1)=jp_2(i+1)=j p 2 ( i + 1 ) = j 如果A(i+1)A(i+1) A ( i + 1 ) 未出现在mim_i m i 中,那么p1(i)p_1(i) p 1 ( i ) ,p2(i)p_2(i) p 2 ( i ) 之间的位置做开头就会多一个元素,不难发现这个元素就是A(p2(i))A(p_2(i)) A ( p 2 ( i ) ) ,因此我们令p1(i+1)=p2(i)+1p_1(i+1) = p_2(i) + 1 p 1 ( i + 1 ) = p 2 ( i ) + 1 ,p1(i+1)p_1(i+1) p 1 ( i + 1 ) 的更新则和上一个条件分支基本一样,然后更新mim_i m i 即可 我们可以发现p1p_1 p 1 和p2p_2 p 2 的更新都是单调递增的,因此每个位置至多被遍历一次,所以总体复杂度是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;
}
} ;