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;
}

http://acm.hdu.edu.cn/showproblem.php?pid=2158

posted @ 2017-08-13 10:48  达达Mr_X  阅读(397)  评论(0)    收藏  举报