自己版本主要思路
用STL中queue维护一个内存为M的队列,再用一个数组维护的哈希表记录单词是否在内存中存在
代码
#include <bits/stdc++.h>
using namespace std;
const int NUM = 1005;
int arr[NUM];
int main(void) {
int M, N;
cin >> M >> N;
queue<int> memory; //内存队列
int count = 0;
for(int i = 0; i < N; i++) {
int num;
cin >> num; //读入当前数字
if(arr[num] == 1) continue; //如果当前数字已经在队列中
else if(arr[num] == 0) { //如果当前数字不在队列中
count++;
if(memory.size() == M) { //先看内存如果满了
arr[memory.front()] = 0; //清掉第一个
memory.pop();
}
memory.push(num); //再放入
arr[num] = 1;
}
}
cout << count << endl;
return 0;
}
手写循环队列版本
//洛谷P1540, 手写循环队列
#include<bits/stdc++.h>
#define N 1003 //队列大小
int Hash[N]={0}; //用Hash检查内存中有没有单词
struct myqueue{
int data[N]; //分配静态空间
/* 如果动态分配,这样写: int *data; */
int head, rear; //队头、队尾
bool init(){ //初始化
/*如果动态分配,这样写:
Q.data = (int *)malloc(N * sizeof(int)) ;
if(!Q.data) return false; */
head = rear = 0;
return true;
}
int size(){ return (rear - head + N) % N;} //返回队列长度
/*为什么要加上N?
由于队列是循环的,当 rear 指向数组末尾时,下一个元素应该指向数组的开头,即 head 索引。因此,如果 rear 索引小于 head 索引,则说明队列已经循环了一圈,需要加上 N 来得到正确的队列长度。
举个例子,假设 rear 索引为 2,head 索引为 5,而数组大小为 8。如果只计算 rear - head,则得到的结果为 -3,这显然不是队列的实际长度。为了得到正确的结果,需要加上数组的大小 N,即 (rear - head + N),这样可以得到 5,即队列的实际长度。*/
/*为什么又要%N?
如果不对结果取模,可能会得到超过数组大小的值,这仍然不是队列的实际长度。因此,需要对 (rear - head + N) 这个计算结果再次取模,以确保它在数组大小的范围内。*/
bool empty(){ //判断队列是否为空
if(size()==0) return true;
else return false;
}
bool push(int e){ //队尾插入新元素。新的rear指向下一个空的位置
if((rear + 1) % N == head ) return false; //队列满
//静态数组实现循环链表,%N均不可少
data[rear] = e;
rear = (rear + 1) % N;
return true;
}
bool pop(int &e){ //删除队头元素,并返回它
利用指针,虽然返回类型是bool,但因为指针所以通过修改指针返回
if(head == rear) return false; //队列空
e = data[head];
head = (head + 1) % N;
return true;
}
int front(){ return data[head]; } //返回队首,但是不删除
}Q;
int main(){
Q.init(); //初始化队列
int m,n; scanf("%d%d",&m,&n);
int cnt = 0;
while(n--){
int en; scanf("%d",&en); //输入一个英文单词
if(!Hash[en]){ //如果内存中没有这个单词
++cnt;
Q.push(en); //单词进队列,放到队列尾部
Hash[en]=1;
while(Q.size()>m){ //内存满了
/*为什么用while,不用if:
如果使用 if 条件语句判断队列的长度是否大于 m,那么当队列的长度恰好等于 m+1 时,也就是内存已经满了,这个单词还没有入队时,就不会执行 while 循环内部的语句了,也就是说不会从队列头部删除元素,导致队列长度一直保持在 m+1,而不是恰好保持在 m。因此,使用 if 条件语句判断队列长度不太合适。
相反,使用 while 循环语句可以保证在队列长度大于 m 的情况下,一直从队头删除元素,直到队列的长度小于等于 m 为止。*/
int tmp; Q.pop(tmp); //删除队头
Hash[tmp] = 0; //从内存中去掉单词
}
}
}
printf("%d\n",cnt);
return 0;
}