Description
约翰的N(1≤N≤100000)只奶牛中出现了K(1≤K≤25000)只爱惹麻烦的坏蛋.奶牛们按一定的顺序排队的时候,这些坏蛋总会站在一起.为了找出这些坏蛋,约翰让他的奶牛排好队进入牛棚,同时需要你的慧眼来识别坏蛋,为了区分,约翰给所有奶牛都发了号牌,上面写着一个1..S(1≤S≤25)之间的数字.虽然这不是一个完美的方法,但也能起一点作用.现在,约翰已经不记得坏蛋们的具体号码.但是凭他的记忆,他给出一个“模式串”.原坏蛋的号码如果相同,模式串中他们的号码依然相同.模式串中坏蛋们之间号码的大小关系也与原号码相同的.比如,对于这样一个模式串:1,4,4,3,,2,1 。原来的6只坏蛋,排最前面的与排最后的号码相同(尽管不一定是1),而且他们的号码在团伙中是最小的.第2,3位置的坏蛋,他们的号码也相同(不一定是4),且是坏蛋团伙中最大的. 现在所有奶牛排成队列,号码依次是这样: 5,6,2,10,10,7,3,2,9存在子串2,10,10,7,3,2,满足模式串的相同关系和大小关系,所以这就是坏蛋团伙, 请找出K个坏蛋的困伙的所有可能性.
Input
第1行输入三个整数N,K,S.接下来N行每行输入一只牛的号码.接下来K行每行输入一个模式串的号码.
Output
第1行输出一个整数B.接下来B行,每行一个整数,表示一种可能下的坏蛋团伙的起始位置.
维护第一个串在每个长度为K的区间内对应位置的排名的hash,与第二个串的排名的hash比较
具体可以用树状数组维护小于指定数的数的个数和大于指定数的数对hash值的贡献,详见代码
#include<cstdio> typedef unsigned long long u64; int n,k,s; int t1[100007],t2[100007],a[100007],b[100007],as[100007],ap=0; u64 pp[100007],h2=0,h1=0,F[100007],fs=0; const u64 p=1844677; void add(int*f,int w,int v){ for(;w<=s;w+=w&-w)f[w]+=v; } int sum(int*f,int w){ int v=0; for(;w;w-=w&-w)v+=f[w]; return v; } void add(int w,u64 v){ fs+=v; for(;w<=s;w+=w&-w)F[w]+=v; } u64 sum(int w){ u64 v=0; for(;w;w-=w&-w)v+=F[w]; return fs-v; } char buf[100007*20],*ptr=buf-1; int _(){ int x=0,c=*++ptr; while(c<48)c=*++ptr; while(c>47)x=x*10+c-48,c=*++ptr; return x; } int main(){ fread(buf,1,sizeof(buf),stdin); n=_();k=_();s=_(); pp[0]=1; for(int i=1;i<=n;++i)pp[i]=pp[i-1]*p; for(int i=1;i<=n;++i)a[i]=_(); for(int i=1;i<=k;++i)b[i]=_(); for(int i=1;i<=k;++i){ add(t1,a[i],1); add(a[i],pp[i]); add(t2,b[i],1); } for(int i=1;i<=k;++i){ h1+=pp[i]*sum(t1,a[i]-1); h2+=pp[i]*sum(t2,b[i]-1); } if(h1==h2)as[ap++]=1; for(int i=k+1,j;i<=n;++i){ j=i-k; h1-=pp[j]*sum(t1,a[j]-1); h1-=sum(a[j]); add(t1,a[j],-1); add(a[j],-pp[j]); h1+=sum(a[i]); h1+=pp[i]*sum(t1,a[i]-1); add(t1,a[i],1); add(a[i],pp[i]); if(h1==h2*pp[j])as[ap++]=j+1; } printf("%d\n",ap); for(int i=0;i<ap;++i)printf("%d\n",as[i]); return 0; }