计蒜客第五场 UCloud 的安全秘钥(中等) (尺取游标法

Posted 狡啮之仰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计蒜客第五场 UCloud 的安全秘钥(中等) (尺取游标法相关的知识,希望对你有一定的参考价值。

每个 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)s1​​,s2​​,...,sn​​(1si​​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)t1​​,t2​​,...,tk​​(1ti​​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 的安全秘钥(中等) (尺取游标法的主要内容,如果未能解决你的问题,请参考以下文章

计蒜客-第五场初赛-第二题 UCloud 的安全秘钥(简单)

2017 计蒜之道 初赛 第五场 UCloud 的安全秘钥(中等)

2017 计蒜之道 初赛 第五场 C. UCloud 的安全秘钥(中等)

2017 计蒜之道 初赛 第五场 B. UCloud 的安全秘钥(简单)

2017 计蒜之道 初赛 第五场 D. UCloud 的安全秘钥(困难)

UCloud 机房的网络搭建(计蒜客初赛第五场)