2019CCPC哈尔滨 L LRU Algorithm(字符串hash)
题意:
题目背景:cache的LRU算法。给定一串长度为n的cache访问序列,多个询问q,每次给mi,表示cache的容量为mi,然后mi个数的序列,问是否存在cache表为这个序列的情况。
题目有多组输入。n<=5000,q<=5000, \(\sum n<=20000,\sum q<=20000,\sum mi<=1e6\)
思路:
按照LRU算法的规律可以很容易发现,对于第i次访问,cache表中的数就是从访问序列第i位往前推,每次未出现的数就加入cache表下一位即可,而cache表的容量大小只是将得到的序列进行截断而已,因此可以预处理出所有位向前推到第1位(假设最多数字种类数为len),1-len所有容量大小所对应的字符串哈希值,时间复杂度为\(O(n^2)\)
每次查询,首先将末尾的0去掉,如果剩余长度l为0,则直接输出yes,否则遍历1-n所有位长度为l的哈希值,若相同则输出Yes(可以再O(N)扫一边确认是否真的相同),若均不相同则输出NO
代码:
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxn=5e3+5;
int seed=13331;
ull has[maxn][maxn];
int a[maxn];
int x[maxn];
int vis[maxn];
void init(int n){
memset(vis,0,sizeof(int)*(n+1));
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)has[i][j]=0;
}
}
int main () {
int T;
scanf("%d",&T);
while(T--){
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
init(n);
for(int i=1;i<=n;i++){
int cnt=0;
for(int j=i;j>=1;j--){
if(vis[a[j]]!=i){
cnt++;
has[i][cnt]=has[i][cnt-1]*seed+a[j];
vis[a[j]]=i;
}
}
}
while(q--){
int mi;
scanf("%d",&mi);
for(int i=1;i<=mi;i++){
scanf("%d",&x[i]);
}
while(x[mi]==0)mi--;
if(mi==0){
puts("Yes");
continue;
}
ull hasx=0;
for(int i=1;i<=mi;i++){
hasx=hasx*seed+x[i];
}
int flag=0;
for(int i=1;i<=n;i++){
if(has[i][mi]==hasx){
memset(vis,0,sizeof(int)*(n+1));
int ok=1;
int cnt=0;
for(int j=i;j>=1 && cnt<mi;j--){
if(!vis[a[j]]){
if(a[j]!=x[++cnt]){
ok=0;
break;
}
vis[a[j]]=1;
}
}
if(ok){
puts("Yes");
flag=1;
break;
}
}
}
if(!flag){
puts("No");
}
}
}
}