解题思路
这是一个区间查找问题,需要找到包含所有不同数字的最小区间。具体要求:
- 序列中的数字范围在
- 找到最小的连续区间,使其包含所有不同的数字
- 如果有多个这样的区间,按照出现顺序输出所有区间
解决方案:
- 使用双指针(滑动窗口)技术
- 对原始数字进行离散化处理,减少空间占用
- 使用计数数组记录每个数字的出现次数
- 维护最小区间长度和所有符合条件的区间
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5000010;
int main() {
int n;
scanf("%d", &n);
// 离散化处理
vector<int> a(n + 1);
map<int, int> mp;
vector<int> count(n + 1, 0);
int unique_nums = 0;
for(int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
if(mp.count(x) == 0) {
mp[x] = unique_nums++;
}
a[i] = mp[x];
count[a[i]]++;
}
// 寻找最小区间
vector<pair<int, int>> intervals;
int min_len = n + 1;
int l = 1, r = n;
// 初始化右边界
while(count[a[r]] > 1) {
count[a[r]]--;
r--;
}
while(true) {
// 调整左边界
while(count[a[l]] > 1) {
count[a[l]]--;
l++;
}
int curr_len = r - l + 1;
if(curr_len < min_len) {
min_len = curr_len;
intervals.clear();
intervals.push_back({l, r});
} else if(curr_len == min_len) {
intervals.push_back({l, r});
}
// 移动窗口
int removed = a[l];
count[a[l]]--;
l++;
if(r == n) break;
while(r < n) {
r++;
count[a[r]]++;
if(a[r] == removed) break;
}
if(a[r] != removed) break;
}
// 输出结果
printf("%d %d\n", min_len, (int)intervals.size());
for(int i = 0; i < intervals.size(); i++) {
if(i > 0) printf(" ");
printf("[%d,%d]", intervals[i].first, intervals[i].second);
}
printf("\n");
return 0;
}
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
// 离散化处理
int[] a = new int[n + 1];
Map<Integer, Integer> mp = new HashMap<>();
int[] count = new int[n + 1];
int uniqueNums = 0;
// 每行读取一个数
for(int i = 1; i <= n; i++) {
int x = Integer.parseInt(br.readLine());
if(!mp.containsKey(x)) {
mp.put(x, uniqueNums++);
}
a[i] = mp.get(x);
count[a[i]]++;
}
// 寻找最小区间
List<int[]> intervals = new ArrayList<>();
int minLen = n + 1;
int l = 1, r = n;
// 初始化右边界
while(count[a[r]] > 1) {
count[a[r]]--;
r--;
}
while(true) {
// 调整左边界
while(count[a[l]] > 1) {
count[a[l]]--;
l++;
}
int currLen = r - l + 1;
if(currLen < minLen) {
minLen = currLen;
intervals.clear();
intervals.add(new int[]{l, r});
} else if(currLen == minLen) {
intervals.add(new int[]{l, r});
}
// 移动窗口
int removed = a[l];
count[a[l]]--;
l++;
if(r == n) break;
while(r < n) {
r++;
count[a[r]]++;
if(a[r] == removed) break;
}
if(a[r] != removed) break;
}
// 使用StringBuilder加快输出
StringBuilder sb = new StringBuilder();
sb.append(minLen).append(" ").append(intervals.size()).append("\n");
for(int i = 0; i < intervals.size(); i++) {
if(i > 0) sb.append(" ");
sb.append("[").append(intervals.get(i)[0]).append(",").append(intervals.get(i)[1]).append("]");
}
System.out.println(sb);
}
}
``` python []
import sys
input = sys.stdin.readline
def solve():
n = int(input())
# 离散化处理
a = [0] * (n + 1)
mp = {}
count = [0] * (n + 1)
unique_nums = 0
nums = [int(input()) for _ in range(n)]
for i in range(n):
x = nums[i]
if x not in mp:
mp[x] = unique_nums
unique_nums += 1
a[i + 1] = mp[x]
count[a[i + 1]] += 1
# 寻找最小区间
intervals = []
min_len = n + 1
l, r = 1, n
# 初始化右边界
while count[a[r]] > 1:
count[a[r]] -= 1
r -= 1
while True:
# 调整左边界
while count[a[l]] > 1:
count[a[l]] -= 1
l += 1
curr_len = r - l + 1
if curr_len < min_len:
min_len = curr_len
intervals = [[l, r]]
elif curr_len == min_len:
intervals.append([l, r])
# 移动窗口
removed = a[l]
count[a[l]] -= 1
l += 1
if r == n:
break
while r < n:
r += 1
count[a[r]] += 1
if a[r] == removed:
break
if a[r] != removed:
break
# 输出结果
print(min_len, len(intervals))
print(' '.join(f'[{interval[0]},{interval[1]}]' for interval in intervals))
solve()
算法及复杂度
- 算法:双指针(滑动窗口)+ 离散化
- 时间复杂度:
- 每个位置最多被访问常数次
- 空间复杂度:
- 需要存储离散化映射和计数数组