HDU 2158 最短区间版大家来找碴 (尺取)
Description
给出一个长度为\(n\)的序列\(A\)和\(m\)次查询,每次查询给出\(q\)个整数,可能有重复,问序列中包含这\(q\)个数的最短区间的长度。
Input
多组用例,以\(0\),\(0\)结束,对于每组用例,第一行给出\(n\)和\(m\),接下来有\(m\)个询问,每个询问第一行给出\(q\),第二行给出\(q\)个整数。\(n \leqslant 10^5\),\(A_i \leqslant 10^5\),\(m \leqslant 10^3\),\(q \leqslant 100\)。
Output
对于每组用例的每个询问,输出满足条件的区间的最小长度。
Sample Input
5 2
1 2 2 3 1
3
1 2 3
3
1 1 3
0 0
Sample Output
3
2
Solution
处理连续区间,尺取问题。依次推进左端点,对于每个左端点,右端点不断向右直到找到第一个满足条件的位置,跟新答案。如果右端点到达序列最右端仍找不到合法区间,则跳出。可用标记数组处理某个值是否出现过。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
int a[N];
bool flag[N];
int num[N];
int main()
{
int n, m;
while (scanf("%d%d", &n, &m), n)
{
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
while (m--)
{
memset(flag, false, sizeof(flag));
int sum = 0;
int q;
scanf("%d", &q);
for (int i = 1; i <= q; i++)
{
int t;
scanf("%d", &t);
if (!flag[t]) { flag[t] = true; sum++; }
}
memset(num, 0, sizeof(num));
int r = 1, cnt = 0, ans = INF;
for (int l = 1; l <= n; l++)
{
while (r <= n && cnt < sum)
{
if (flag[a[r]])
{
num[a[r]]++;
if (num[a[r]] == 1) cnt++;
}
r++;
}
if (cnt < sum) break;
ans = min(ans, r - l);
if (flag[a[l]])
{
num[a[l]]--;
if (num[a[l]] == 0) cnt--;
}
}
printf("%d\n", ans);
}
}
return 0;
}