题解 CF666E 【Forensic Examination】
xtx1092515503 · · 题解
CF666E Forensic Examination
这里是一种后缀数组+莫队的奇妙解法~~~
首先,常规套路就是将所有串中间插上一个字符然后并一起跑后缀排序。这样跑完之后我们发现,如果我们要求英文题面中“禁忌串”中一段子串
这样搞完之后,我们发现问题就被转化为:在后缀数组中的一段区间里,来自哪本英文题面中“小册子”的后缀是出现最多的?
显然这是一个区间众数问题,常规解法是分块(这题)或者莫队。但是因为这题并没有强制在线,并且对众数的值域是有限制的(只有来自
因为对值域有要求,我们考虑再关于值域分块。对于每个块,我们需要记录块内出现次数最多的那本小册子出现了多少次。显然当莫队往区间里加入元素时很好处理,直接取
显然删除一个元素时,众数出现次数最多减一。于是我们可以针对每个块开一个桶,记录块中有多少元素的出现次数为
然后最终统计答案时,就是一个常规的分块,整块直接调用块中众数,散块暴力扫过即可。
我们下面来分析复杂度。按照我们代码中的写法,我们设
然后是空间部分。我们有ST表的
如果你是空间大小的狂热追求者的话,我们还有一种优化至 std::vector<int> 来维护桶,每次在结尾 pop 或 push 即可。因为所有“小册子”的总长只有
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=600100;
int A,B,id[N];
struct Node{
int id,val;
Node(int x=0x3f3f3f3f,int y=0){id=x,val=y;}
friend bool operator <(const Node &x,const Node &y){
return x.val!=y.val?x.val<y.val:x.id>y.id;
}
}res[N];
namespace Suffix_Array{
int x[N],y[N],buc[N],sa[N],ht[N],rk[N],s[N],n,m,LG[N],mn[N][20];
char str[N];
bool mat(int a,int b,int k){
if(y[a]!=y[b])return false;
if((a+k<n)^(b+k<n))return false;
if((a+k<n)&&(b+k<n))return y[a+k]==y[b+k];
return true;
}
void SA(){
for(int i=0;i<n;i++)buc[x[i]=s[i]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n-1;i>=0;i--)sa[--buc[x[i]]]=i;
for(int k=1;k<n;k<<=1){
int num=0;
for(int i=n-k;i<n;i++)y[num++]=i;
for(int i=0;i<n;i++)if(sa[i]>=k)y[num++]=sa[i]-k;
for(int i=0;i<=m;i++)buc[i]=0;
for(int i=0;i<n;i++)buc[x[y[i]]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n-1;i>=0;i--)sa[--buc[x[y[i]]]]=y[i];
swap(x,y);
x[sa[0]]=num=0;
for(int i=1;i<n;i++)x[sa[i]]=mat(sa[i],sa[i-1],k)?num:++num;
if(num>=n-1)break;
m=num;
}
for(int i=0;i<n;i++)rk[sa[i]]=i;
for(int i=0,k=0;i<n;i++){
if(!rk[i])continue;
if(k)k--;
int j=sa[rk[i]-1];
while(i+k<n&&j+k<n&&s[i+k]==s[j+k])k++;
ht[rk[i]]=k;
}
for(int i=2;i<N;i++)LG[i]=LG[i>>1]+1;
for(int i=1;i<N;i++)mn[i][0]=ht[i];
for(int j=1;j<=LG[N-1];j++)for(int i=1;i+(1<<j)-1<N;i++)mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
}
int LCP(int x,int y){
if(x>y)swap(x,y);
x++;
int k=LG[y-x+1];
return min(mn[x][k],mn[y-(1<<k)+1][k]);
}
void Range(int x,int k,int &L,int &R){
int l,r;
x=rk[x];
l=0,r=x;
while(l<r){
int mid=(l+r)>>1;
if(LCP(mid,x)>=k)r=mid;
else l=mid+1;
}
L=r;
l=x,r=n-1;
while(l<r){
int mid=(l+r+1)>>1;
if(LCP(mid,x)>=k)l=mid;
else r=mid-1;
}
R=l;
}
}
using namespace Suffix_Array;
const int BBB=800;
const int CCC=300;
int BLK[50100],lp[50100];
struct Query{
int l,r,ql,qr,id;
friend bool operator <(const Query &x,const Query &y){
if(x.r/BBB!=y.r/BBB)return x.r<y.r;
return (x.r/BBB)&1?x.l<y.l:x.l>y.l;
}
}q[500100];
int cnt[50100],occ[200][50100],lim[200];
void Push(int x){
x=id[sa[x]];
if(x==-1)return;
occ[BLK[x]][cnt[x]]--;
cnt[x]++;
occ[BLK[x]][cnt[x]]++;
lim[BLK[x]]=max(lim[BLK[x]],cnt[x]);
}
void Pop(int x){
x=id[sa[x]];
if(x==-1)return;
occ[BLK[x]][cnt[x]]--;
cnt[x]--;
occ[BLK[x]][cnt[x]]++;
if(!occ[BLK[x]][lim[BLK[x]]])lim[BLK[x]]--;
}
void Ask(int l,int r,Node &ans){
r++;
if(BLK[l]==BLK[r]){for(int i=l;i<r;i++)ans=max(ans,Node(i,cnt[i]));return;}
int pos=BLK[A];
for(int i=BLK[l]+1;i<BLK[r];i++)if(lim[i]>lim[pos])pos=i;
if(pos!=BLK[A])for(int i=lp[pos];i<lp[pos+1];i++)ans=max(ans,Node(i,cnt[i]));
for(int i=l;i<lp[BLK[l]+1];i++)ans=max(ans,Node(i,cnt[i]));
for(int i=lp[BLK[r]];i<r;i++)ans=max(ans,Node(i,cnt[i]));
}
int main(){
scanf("%s",str),m=strlen(str);for(int i=0;i<m;i++)s[n]=str[i]-'a'+1,id[n]=-1,n++;
scanf("%d",&A);
for(int i=0;i<A;i++){
s[n]=i+26,id[n]=-1,n++;
scanf("%s",str),m=strlen(str);
for(int j=0;j<m;j++)s[n]=str[j]-'a'+1,id[n]=i,n++;
}
// for(int i=0;i<n;i++)printf("%d ",s[i]);puts("");
m=A+26;
SA();
scanf("%d",&m);
for(int i=1,a,b,c,d;i<=m;i++){
scanf("%d%d%d%d",&a,&b,&c,&d);
Range(c-1,d-c+1,q[i].l,q[i].r);
q[i].ql=a-1,q[i].qr=b-1;
q[i].id=i;
}
for(int i=0;i<A;i++)BLK[i]=i/CCC;
for(int i=0;i<=BLK[A-1];i++)lp[i]=i*CCC;
BLK[A]=BLK[A-1]+1;
lp[BLK[A]]=A;
sort(q+1,q+m+1);
Push(0);
for(int i=1,L=0,R=0;i<=m;i++){
while(L>q[i].l)Push(--L);
while(R<q[i].r)Push(++R);
while(L<q[i].l)Pop(L++);
while(R>q[i].r)Pop(R--);
Ask(q[i].ql,q[i].qr,res[q[i].id]);
}
for(int i=1;i<=m;i++)printf("%d %d\n",res[i].id+1,res[i].val);
return 0;
}