窗口判断子数组排列

题目

给定一个数组,试求有多少个长度为\(k\)连续子数组是排列。
https://ac.nowcoder.com/acm/problem/273933

Input

第一行输入两个正整数\(n\),\(k\),代表小红拿到的数组大小和连续子数组的大小。
第二行输入\(n\)个正整数\(a_i\),代表数组中的元素
其中\(1 \leq k \leq n \leq 10^5\)\(1 \leq a_i \leq 10^5\)

5 2
1 2 1 1 2

Output

一个整数,代表长度为\(k\)的连续子数组是排列的数量

3

说明
\(3\)个长度为\(2\)的连续子数组是排列:两个\([1,2]\)和一个\([2,1]\)

题解

解题思路

此题核心思想为用窗口分割数组,并实现线性时间复杂度遍历。
在操作某一个窗口的时候,根据排列的定义,当\(map[a[i]]==1\)时,可将某一个窗口的第\(i\)个位置标记为合法,\(cnt++\),若\(cnt==k\)则表示一个合法排序,\(ans++\)
注意,在窗口移动的时候,需要处理\(a[i-k]\),即原窗口的第一个数。
注意,在开始遍历之前,需要先对第一个长度为\(k\)的窗口进行判断。

Code

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false); cin.tie(nullptr);
using namespace std;
typedef long long LL;

signed main(){
	IOS;
	int n,k;cin>>n>>k;
    vector<int> a(n+1);
    for(int i=1;i<=n;i++) cin>>a[i];
    map<int,int> mp;
    LL ans=0,cnt=0;
    //判断第一个长度为k的子串是否为排列
    for(int i=1;i<=k;i++){
        mp[a[i]]++;
        if(a[i]>=1 and a[i]<=k and mp[a[i]]==1) cnt++;
    }
    if(cnt==k) ans++;
    //利用长度为k的窗口开始枚举
    for(int i=k+1;i<=n;i++){
        mp[a[i]]++;
        if(a[i]>=1 and a[i]<=k and mp[a[i]]==1) cnt++;
        if(a[i-k]>=1 and a[i]<=k and mp[a[i-k]]==1) cnt--;
        mp[a[i-k]]--;
        if(cnt==k) ans++;
    }
    cout<<ans<<endl;
	return 0;
}

posted on 2024-06-03 20:40  TaopiTTT  阅读(4)  评论(0编辑  收藏  举报