1. 程式人生 > >計蒜客第五場 UCloud 的安全秘鑰(中等) (尺取遊標法

計蒜客第五場 UCloud 的安全秘鑰(中等) (尺取遊標法

sca http 數組 desc 出現 滿足 說明 tom tor

每個 UCloud 用戶會構造一個由數字序列組成的秘鑰,用於對服務器進行各種操作。作為一家安全可信的雲計算平臺,秘鑰的安全性至關重要。因此,UCloud 每年會對用戶的秘鑰進行安全性評估,具體的評估方法如下:

首先,定義兩個由數字序列組成的秘鑰 aa 和 bb近似匹配(\approx≈) 的關系。aa 和 bb 近似匹配當且僅當同時滿足以下兩個條件:

  • |a|=|b|a=b∣,即 aa 串和 bb 串長度相等。
  • 對於每種數字 cc,cc 在 aa 中出現的次數等於 cc 在 bb 中出現的次數。

此時,我們就稱 aa 和 bb 近似匹配,即 a \approx bab。例如,(1,3,1,1,2)\approx(2,1,3,1,1)(1,3,1,1,2)(2,1,3,1,1)。

UCloud 每年會收集若幹不安全秘鑰,這些秘鑰組成了不安全秘鑰集合 TT。對於一個秘鑰 ss 和集合 TT 中的秘鑰 tt 來說,它們的相似值定義為:ss 的所有連續子串中與 tt 近似匹配的個數。相似值越高,說明秘鑰 ss 越不安全。對於不安全秘鑰集合 TT 中的每個秘鑰 tt,你需要輸出它和秘鑰 ss 的相似值,用來對用戶秘鑰的安全性進行分析。

輸入格式

第一行包含一個正整數 nn,表示 ss 串的長度。

第二行包含 nn 個正整數 s_1,s_2,...,s_n(1\leq s_i\leq n)s?1??,s?2??,...,s?n??(1s?i??n),表示 ss 串。

接下來一行包含一個正整數 mm,表示詢問的個數。

接下來 mm 個部分:

每個部分第一行包含一個正整數 k(1\leq k\leq n)k(1kn),表示每個 tt 串的長度。

每個部分第二行包含 kk 個正整數 t_1,t_2,...,t_k(1\leq t_i\leq n)t?1??,t?2??,...,t?k??(1t?i??n),表示 TT 中的一個串 tt。

輸入數據保證 TT 中所有串長度之和不超過 200000200000。

對於簡單版本:1\leq n,m\leq 1001n,m100;

對於中等版本:1\leq n\leq 50000,1\leq m\leq 5001n50000,1m500;

對於困難版本:1 \le n \le 50000, 1 \le m \le 1000001n50000,1m100000。

輸出格式

輸出 mm 行,每行一個整數,即與 TT 中每個串 tt近似匹配的 ss 的子串數量。

樣例解釋

對於第一個詢問,(3,2,1,3)\approx(2,3,1,3)(3,2,1,3)(2,3,1,3),(3,2,1,3)\approx(3,1,3,2)(3,2,1,3)(3,1,3,2);

對於第二個詢問,(1,3)\approx(3,1)(1,3)(3,1),(1,3)\approx(1,3)(1,3)(1,3);

對於第三個詢問,(3,2)\approx(2,3)(3,2)(2,3),(3,2)\approx(3,2)(3,2)(3,2)。

樣例輸入

5
2 3 1 3 2
3
4
3 2 1 3
2
1 3
2
3 2

樣例輸出

2
2
2

對於每個詢問
實際上我們的想法是能通過類似尺取的方式,將每一個長度為len的區間取出來,能hash出來,O(1)查詢hash值,從而做到查詢O(n*m)
實際上這個hash沒想出來,但是這裏有一種巧妙的尺取法,
對於每個詢問,我們記錄下每種數字的個數,標記數組是這樣一個東西
技術分享 (下面這個v數組就像遊標一樣,再加上我用了尺取法,所以就起名叫尺取遊標法啦

如果t串中1的個數比s的某一子串多那麽v[1]>0

如果比它少就小於零,|v[1]|代表相差元素個數

我們記錄一下t串中v[]不為零的個數cnt

則當且僅當某串的各串數量和t串相等時,cnt為0

簡單考慮幾種情況,某串種類比t多,cnt肯定會變大

某串的種類比t少,cnt可能變大也可能變小,但不為0,具體要看每種的數量

某串種類和t相等,但某類數字出現過多或過少,cnt不為0

所以我們發現動態維護cnt是可行的

區間滑動改變一個左端點,改變一個右端點,動態維護v數組,並且根據v修改cnt

如果cnt為0一次,那麽對答案的貢獻加一

#include <stdio.h>
#include <cstring>
#include <vector>
#include <map>
using namespace std;
const int maxn=5e4+7;
typedef long long ll;
int s[maxn],t[maxn],ns,nt,m,v[maxn];
int main(){
    scanf("%d",&ns);
    for(int i=0;i<ns;++i) scanf("%d",s+i);
    scanf("%d",&m);
    while(m--){
        scanf("%d",&nt);
        for(int i=0;i<nt;++i) scanf("%d",t+i);
        memset(v,0,sizeof(v));
        int cnt=0;
        for(int i=0;i<nt;++i){
            v[t[i]]++;
            if(v[t[i]]==1) cnt++;
        }
        for(int i=0;i<nt;++i){
            if(v[s[i]]==0) cnt++;
            else if(v[s[i]]==1) cnt--;
            v[s[i]]--;
        }
        int ans=0,l=1,r=nt;
        if(!cnt) ans++;
        while(r<ns){
            if(v[s[r]]==0) cnt++;
            else if(v[s[r]]==1) cnt--;
            v[s[r]]--;
            if(v[s[l-1]]==-1) cnt--;
            else if(v[s[l-1]]==0) cnt++;
            v[s[l-1]]++;
            if(!cnt) ans++;
            l++;r++;
        }
        printf("%d\n",ans);
    }
    return 0;
}



計蒜客第五場 UCloud 的安全秘鑰(中等) (尺取遊標法