2019CCPC哈爾濱 L LRU Algorithm(字串hash)
阿新 • • 發佈:2020-10-04
題意:
題目背景: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"); } } } }