首页 > 试题广场 >

合法连续子段

[编程题]合法连续子段
  • 热度指数:2268 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
小强有一个长度为的数组和正整数.
他想请你帮他计算数组中有多少个连续子区间[l,r],其区间内存在某个元素出现的次数不小于次?
例如数组,那么区间[1,3],[1,4],[1,5],[2,4],[2,5]都是满足条件的区间,但区间[3,4]等都是不满足条件的.

输入描述:
第一行输入两个正整数.
第二行输入n个正整数.



输出描述:
输出一个整数表示答案.
示例1

输入

5 2
1 2 1 2 3

输出

5

说明

满足条件的区间为[1,3],[1,4],[1,5],[2,4],[2,5].
把每一个值的位置用二维数组vector记录下来,当vector[x]的元素数量达到m时,例如m=3,现在我们在3,6,9这三个位置元素都是x,此时[1,9]、[2,9],[3,9]都是满足条件的区间。我们将3从vector中删除,这样未来如果再出现x时,起始位置就是6。有6个区间满足条件。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n,m,a;
ll ans=0;
vector<int>v[400005];
int main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int i,j=1,best=0;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        cin>>a;/**< a值下标存储在v[a]中 */
        v[a].push_back(i);
        /**< 第i个位置时a的数量达到了m,将i作为右端点此时a的第一个位置及其之前的位置都满足条件 */
        if(v[a].size()==m)
        { /**< 我们取满足条件的数字中下标最靠右的 */
            best=max(best,v[a][0]);
            v[a].erase(v[a].begin());
        }
        ans+=best;
    }
    cout<<ans;
    return 0;
}


发表于 2021-05-02 18:41:04 回复(0)
Python版本,算啊发复杂度过大,供参考
def judge(aa, m):
    list1 = []

    for i in range(len(aa)):
        cnt1 = 0
        for j in range(len(aa)):
            if aa[i] == aa[j]:
                cnt1 += 1
        list1.append(cnt1)

    if max(list1) >= m:
        return True
    else:
        return False

inp = input().split()
n = int(inp[0])
m = int(inp[1])

a = input().split()
cnt = 0

for i in range(n-1):
    for j in range(i+1, n):
        if judge(a[i: j+1], m) == True:
            cnt += 1

print(cnt)


发表于 2022-03-19 11:42:07 回复(0)
n, m = map(int, input().split())
arr = list(map(int, input().split()))
cnt = [0] * (n + 5)
cnt[arr[0]] = 1
r, ans = 1, 0
for l in range(1, n + 1):
    if r <= n and cnt[arr[r - 1]] < m:
        r += 1
        while r <= n:
            cnt[arr[r - 1]] += 1
            if cnt[arr[r - 1]] >= m:
                break
            r += 1
    ans += (n - r + 1)
    cnt[arr[l - 1]] -= 1
print(ans)

发表于 2021-04-29 21:05:57 回复(0)

滑动窗口去处理,但是要注意一个子区间满足时,要不断移动左边界,计算符合要求的全部区间。例如m = 2,nums = [1,2,1,3,4]时,滑动窗口刚刚覆盖[1,2,1]时满足要求,但是[1,2,1,3],[1,2,1,3,4]同时也满足要求。

import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int len = s.nextInt(), m = s.nextInt();
        int[] nums = new int[len];
        for (int i = 0; i < len; i++) {
            nums[i] = s.nextInt();
        }
        int left = 0;
        long ans = 0;
        Map<Integer, Integer> map = new HashMap<>();
        boolean find = false;
        for (int right = 0; right < len; right++) {
            int times = map.getOrDefault(nums[right], 0);
            times++;
            map.put(nums[right], times);
            if (times == m) {
                find = true;
            }
            while (find) {
                ans += len - right;
                int leftTimes = map.get(nums[left]);
                leftTimes--;
                map.put(nums[left], leftTimes);
                if (nums[left] == nums[right] && leftTimes < m) {
                    find = false;
                }
                left++;
            }
        }
        System.out.println(ans);
    }
}
编辑于 2021-10-05 10:19:08 回复(0)
c++ 双指针法
(比较扯的一点是,int 装不下,一个测试都过不了,题目也不给个提示,绝了!)
#include<iostream>
#include<algorithm>
#include<vector>
#include<unordered_map>
using namespace std;

int main(){
    int n = 0, m=0;
    cin>>n>>m;
    vector<int> nums(n);
    for(int i =0;i<n;i++){
        cin>>nums[i];
    }
    long long left = 0, right = 0, res = 0;
    unordered_map<int, int> mymap;
    mymap[nums[right]]++;
    while(left<n){
        while(mymap[nums[right]]<m){
            right ++;
            if(right == n)
                break;
            mymap[nums[right]] ++;
        }
        if(right == n){
            break;
        }
        res += (n - right);
        mymap[nums[left]]--;
        left++;
    }
    cout<<res<<endl;
    return 0;
}


发表于 2021-07-04 03:05:25 回复(2)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int n,m;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin >> n >> m){
        vector<int> a(n);
        for(int i = 0;i < n;i++){
            cin >> a[i];
        }
        //先找到刚好只有一个最大重复为m的所有区间。每个区间求向后的区间数
        unordered_map<int,int> cnt;
        ll res = 0;
        int l = 0, r = 0;
        while(r < n){
            cnt[a[r]]++;
            while(l<=r && cnt[a[r]] >= m){
                res += (n-r);
                cnt[a[l]]--;
                l++;
            }
            r++;
        }
        
        printf("%ld\n", res);
    }
}
经典滑动窗口。但是我被这题创死了。原因是,结果是长整型,一般的题目不应该提示取模吗?可见出题者不讲武德。然后边界条件l<=r错了。
发表于 2022-02-26 11:18:41 回复(3)
??????测了3个多小时 怎么测都是对的,一跑代码就错,原来你T , M 的所有用例都用Int装不了是吧????
发表于 2021-08-27 15:09:48 回复(0)
import sys
import math

a = input().split()
n = int(a[0])
m = int(a[1])
a = list(int(i) for i in input().split())

res = 0
i = 0
dic = dict()
j = 0

while j < n:
        try:
            dic[a[j]] += 1            
        except:
            dic[a[j]] = 1

        while dic[a[j]] == m:
            res += n-j
            dic[a[i]] -= 1
            i += 1
        j += 1

print(res)

发表于 2023-04-01 18:00:32 回复(0)
滑动窗口,结果用long存,找半天才发现

import java.util.HashMap;
import java.util.Scanner;
 
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        int[] list = new int[n];
        for(int i=0;i<n;++i){
            list[i]=in.nextInt();
        }
        long ans=0;
        int i=0,j=0;
        HashMap<Integer, Integer> hashMap=new HashMap<>();
        while(j<n){
            if(!hashMap.containsKey(list[j])){
                hashMap.put(list[j],1);
            }
            else {
                hashMap.put(list[j], hashMap.get(list[j]) + 1);
            }
            while (hashMap.get(list[j]) >= m) {
                ans += (long)(n - j);
                hashMap.put(list[i], hashMap.get(list[i]) - 1);
                i++;
            }
            j++;
        }
        System.out.println(ans);
        in.close();
    }
}


发表于 2023-03-14 16:51:43 回复(0)
n, m = map(int, input().split())
nums = list(map(int, input().split()))

from collections import defaultdict
counter = defaultdict(int)
res = 0
j = 0
for i in range(n):
    counter[nums[i]] += 1
    while counter[nums[i]] == m:
        counter[nums[j]] -= 1
        j += 1
    res += j
print(res)


发表于 2022-09-09 18:02:42 回复(0)
参考寒冰-侠客
1.从左到右记录每个元素出现的下标(从1开始):哈希表加双端队列;
2.维护一个变量satisfy:表示当前枚举的下标 x 和 小于等于satisfy 的所有下标(当然要大于 0)组成的区间都是合法区间, 且satisfy 是满足上述条件的最大值,枚举坐标的时候累加合法区间数量;
3.当某个元素出现的次数等于 m 时,更新satisfy, 并将该元素的在出现的队头的坐标删去。
时间复杂度 O(n)
空间复杂度 O(n) 
#include <iostream>
#include <unordered_map>
#include <deque>
using namespace std;

unordered_map<int, deque<int>> f;
long long ans = 0;
int n, m;

int main()
{
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n >> m;
    int satisfy = 0;
    for(int i = 1; i <= n; i ++ )
    {
        int x;
        cin >> x;
        f[x].push_back(i);
        if(f[x].size() == m)
        {
            satisfy = max(satisfy, f[x][0]);
            f[x].pop_front();
        }
        ans += satisfy;
    }
    cout << ans;
    return 0;
}


编辑于 2022-07-01 15:42:52 回复(0)
#include<bits/stdc++.h>
using namespace std;

int main(){
    uint64_t res = 0;
    int n, m;
    cin >> n >> m;
    int nums[n];
    for(int i = 0;i < n; i++) {
        cin >> nums[i];
    }
    unordered_map<int, int> cnt;
    for(int right = 0, left = 0; right < n; right++) {
        if(++cnt[nums[right]] == m) {
            res += (n-right);
            
            //当左窗口右缩的时候不用担心  m = 2  [4 3 1 2 1 2 3 7]
            //像 3 3会被漏算的情况 会被算进去的
            //小于等于是因为 m = 1 的样例
            while(left <= right && cnt[nums[right]] >= m) {
                cnt[nums[left]]--;
                left++;
                if(cnt[nums[right]] == m) {
                    res += (n-right);
                }
            }
        }
    }
    
    cout << res;
    return 0;
}

发表于 2022-03-10 18:57:21 回复(0)
双指针
from collections import defaultdict
n, m = list(map(int, input().split()))
arr = list(map(int, input().split()))

mem = defaultdict(int)
ans = 0
i,j = 0,0
max_num = 0

while j < n:
    mem[arr[j]] += 1
    max_num = max(max_num, mem[arr[j]])
    if max_num == m:
        while max_num == m:
            ans += n-j
            if mem[arr[i]] == max_num:
                max_num -= 1
            mem[arr[i]] -= 1
            i += 1
    j+=1
print(ans)


发表于 2022-03-08 18:43:44 回复(0)
区间[l,r];
以数组元素a[i]为key值,出现的次数为value值;先从0开始遍历,直至遍历至有元素等于m,那么[0,r]符合要求,意味着有n-r符合要求;
确定[0,r]符合要求后,左下标l ++,并且存储的key值a[l]出现的次数--;如果仍然有满足要求的区间,则意味着有 n-r符合要求;
如果不符合要求 则右下标向右遍历,直至再次找到符合要求的区间。
 public static void main(String[] args) {
        //输入
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int[] a = new int[n];
        for(int i = 0; i < n; i++){
            a[i] = sc.nextInt();
        }
        //处理
        Map<Integer,Integer> map = new HashMap<>();
        int res = 0;
        int l = 0, r = 0;
        while (r < n){
            map.put(a[r], map.getOrDefault(a[r], 0)+1);
            while(l <= r && map.get(a[r]) >= m){//满足要求的区间
                res += n - r;
                map.put(a[l],map.get(a[l])-1);
                l--;
            }
            r++;
        }
        //输出
        System.out.println(res);
    }



发表于 2022-03-08 17:28:56 回复(0)
滑动窗口 python版
import collections
n, m = map(int, input().split())
res = list(map(int, input().split()))
left, right = 0, 0
count = collections.Counter()
ans = 0
judge = False
v = None
while left <= right < n:
    if judge:
        ans += n-right
        count[res[left]] -= 1
        left += 1      
        if count[v] < m:
            judge = False
            right += 1
    else:
        count[res[right]] += 1
        if count[res[right]] >= m:
            v = res[right]
            judge = True
        else:
            right += 1
print(ans)
发表于 2022-03-02 17:08:33 回复(0)
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
    int n,m;
    cin>>n>>m;
    int a[n];
    int b[n+1];
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    memset(b,0,sizeof(b));
    int from=0;
    int to=0;
    int flag=0;
    long long cnt=0;
    while(from<n && to<n)
    {

        b[a[to]]++;
        if(b[a[to]]==m)
        {
            //cout<<"***"<<endl;
            flag=1;
            cnt+=n-to;
            b[a[from]]--;
            from++;
            b[a[to]]--;
        }
        else
        {
            to++;
        }
        
    }
    cout<<cnt<<endl;
}
发表于 2022-02-05 12:56:29 回复(0)
双指针+贡献计算即可,贡献计算时要保证当前是满足要求的最小区间,同时注意不要重复计算前面计算过的贡献。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[600000];
int cnt[600000];

signed main() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    int ans = 0;
    int pre = 1;
    for (int i = 1, j = 0;;) {
        do {
            if (j >= n) break;
            cnt[a[++j]]++;
        }while (cnt[a[j]] < m);

        while (cnt[a[j]] >= m) {
            if (a[i] != a[j]) {
                cnt[a[i]]--;
                i++;
            } else break;
        }
        if (cnt[a[j]] >= m)
            ans += (i-pre+1) *  (n - j + 1);
//        cout << i << ' ' << j << endl;
        if (j >= n) break;
        cnt[a[i]]--;i++;
        pre = i;
    }
    cout << ans << endl;
}


发表于 2022-02-04 19:02:55 回复(0)
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    map<int, vector<int> > mp;
    int temp;
    int l = -1;
    long long res = 0;//这里要用long long否则不过
    for (int r = 0;r < n;r++)
    {
        cin >> temp;
        mp[temp].push_back(r);
        if (mp[temp].size()>=m)
        {
            l = max(l, mp[temp][mp[temp].size() - m]);
        }
        if (l!=-1)
        {
            res += (l + 1);
        }
    }
    cout << res << endl;
}
发表于 2021-09-22 20:25:13 回复(0)
说好的输出整数呢,是不是玩不起😅
发表于 2021-09-09 16:48:28 回复(0)
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Scanner;

/**
 * @auther slience
 */
public class Main {


    public static long solve(int[] arr,int m){
       long res = 0;
       // 存放每个数出现的次数
        HashMap<Integer,Integer> map1 = new HashMap<>();
        HashSet<Integer> set =  new HashSet<>();
        int L=0;
        int R=L;
          while (L<arr.length){
             while(R<arr.length&&(set.size()==0)) {
                 if (map1.containsKey(arr[R])) {
                     map1.put(arr[R], map1.get(arr[R]) + 1);
                 } else {
                     map1.put(arr[R], 1);
                 }
                 if (map1.get(arr[R]) >= m) {
                     set.add(arr[R]);
                 }
                 R++;
             }
             if(set.size()!=0){
                 res+=arr.length-R+1;
             }
             map1.put(arr[L],map1.get(arr[L])-1);
             Integer number = map1.get(arr[L]);
             if(number<m){
                set.remove(arr[L]);
             }
             L++;
      }
        return res;
    }




        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            int n = scanner.nextInt();
            int m = scanner.nextInt();
            int[] arr =new  int[n];
            for(int i=0;i<arr.length;i++){
                arr[i]=scanner.nextInt();
            }
            System.out.println(solve(arr, m));
        }




}
大致思路:双指针模拟过程 解题 常坑人点用long接住
发表于 2021-08-23 17:09:29 回复(0)

热门推荐

通过挑战的用户