http://poj.org/problem?id=3320
题意:
给出一串数字,要求包含所有数字的最短长度。
思路:
哈希一直不是很会用,这道题也是参考了别人的代码,想了很久。
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std; const int PRIME = ; int n;
int len; //开散列法,也就是用链表来存储,所以下面的len是从PRIME开始的,因为前面的都是头结点。 struct node //key是数值大小,num是该key出现的次数,因为是链表存储,所以next指向下一个结点
{
int key;
int num;
int next;
}p[]; int a[]; int _hash(int num)
{
int k = num%PRIME; //取余
while (p[k].next != -) //该数值已经出现过了
{
if (num > p[p[k].next].key) break; //按递减的方式排列
else if (num == p[p[k].next].key) return p[k].next; //如果已经出现过了,直接返回
k = p[k].next;
}
//没有出现过,添加新的结点
p[len].key = num;
p[len].num = ;
p[len].next = p[k].next;
p[k].next = len;
len++;
return len - ;
} int main()
{
//freopen("D:\\txt.txt", "r", stdin);
while (~scanf("%d", &n) && n)
{
for (int i = ; i < PRIME; i++)
p[i].next = -;
len = PRIME;
int left = ;
int ans;
for (int i = ; i < n; i++)
{
scanf("%d", &a[i]);
int temp = _hash(a[i]);
p[temp].num++;
if (p[temp].num == ) //说明第一次出现,所以肯定要包括进去,此时ans肯定等于i-left+1
{
ans = i - left + ;
continue;
}
//如果之前已经出现过
temp = _hash(a[left]);
//如果left指向的数值后面还有出现,那么可以右移一位
while (left<n - && p[temp].num>)
{
p[temp].num--;
left++;
temp = _hash(a[left]);
}
if (ans > i - left + ) ans = i - left + ;
}
printf("%d\n", ans);
}
return ;
}
接下来再附上尺取法的做法,主要思路和上面是差不多的。
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<set>
#include<map>
using namespace std; const int maxn = + ; int n;
int x[maxn];
set<int> p;
map<int, int> q; int main()
{
//freopen("D:\\txt.txt", "r", stdin);
while (~scanf("%d", &n))
{
p.clear();
q.clear();
for (int i = ; i <= n; i++)
{
scanf("%d", &x[i]);
} for (int i = ; i <= n; i++)
p.insert(x[i]); int ans = n;
int total = p.size(); //不重复的数
int sum = ;
int left = , right = ;
while (left<=n && right <= n)
{
if (q[x[right]] == ) sum++; //这个数没有出现过
q[x[right]]++; while (q[x[left]] > )
{
q[x[left]]--;
left++;
}
if (sum == total)
{
ans = min(ans, right - left + );
if (q[x[left]] == ) sum--;
q[x[left]]--;
left++;
}
right++;
}
printf("%d\n", ans);
}
return ;
}