Codeforces 666E. Forensic Examination 广义后缀自动机+线段树合并+倍增

题意

给你一个母串和很多询问串,问母串的一段区间在一段连续询问串中出现最多的次数和询问串的位置

分析

快退役选手些不动这种东西啊。。
就对所有串建广义后缀自动机,然后建一棵线段树,位置为下标,然后按照father树合并上去,倍增找到相应位置就行了

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1100010;
inline int read()
{
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}
int step[N],g[N][27],fail[N][21],last,tot; int pos[N];
int lc[N*30],rc[N*30],c[N*30],rt[N],tt,n;
void link(int &u,int L,int R,int k)
{
  if(!u) u=++tt;
  if(L==R){c[u]++; return ;}
  int mid=(L+R)>>1;
  if(k<=mid) link(lc[u],L,mid,k);
  else link(rc[u],mid+1,R,k);
  c[u] = max(c[lc[u]] , c[rc[u]]);
}
void merge(int &u1,int u2,int L,int R)
{
  if(!u2) return;
  if(!u1){u1=u2; return ;}
  int u = ++tt; lc[u] = lc[u1]; rc[u] = rc[u1]; // printf("%d %d %d %d\n",u1,u2,L,R);
  if(L==R){c[u] = c[u1] + c[u2]; u1 = u; return ;}
  int mid = (L+R)>>1;
  merge(lc[u],lc[u2],L,mid);
  merge(rc[u],rc[u2],mid+1,R);
  c[u] = max(c[lc[u]],c[rc[u]]); u1 = u;
}

int idd,mx;

int qry(int u,int L,int R,int l,int r)
{
  if(L==l && R==r) return c[u];
  int mid=(L+R)>>1;
  if(r<=mid) return qry(lc[u],L,mid,l,r);
  else if(l>mid) return qry(rc[u],mid+1,R,l,r);
  else return max(qry(lc[u],L,mid,l,mid),qry(rc[u],mid+1,R,mid+1,r));
}

int find(int u,int L,int R)
{
  if(L==R) return L;
  int mid=(L+R)>>1;
  if(c[lc[u]] == mx) return find(lc[u],L,mid);
  else return find(rc[u],mid+1,R);
}

int qry2(int u,int L,int R,int l,int r)
{
  if(L==l && R==r){if(c[u] == mx) return find(u,L,R); else return -1;}
  int mid=(L+R)>>1;
  if(r<=mid) return qry2(lc[u],L,mid,l,r);
  else if(l>mid) return qry2(rc[u],mid+1,R,l,r);
  else
  {
    int x = qry2(lc[u],L,mid,l,mid);
    if(x==-1) return qry2(rc[u],mid+1,R,mid+1,r);
    else return x;
  }
}

char ss[N];
void insert(int nx,int id)
{
  int p,np,q,nq;
  if(g[last][nx])
  {
    p=last; q = g[last][nx];
    if(step[q] == step[p]+1){last=q; if(id) link(rt[q],1,n,id);}
    else
    {
      nq = ++tot; step[nq] = step[p] + 1; last = nq; if(id) link(rt[nq],1,n,id);
      memcpy(g[nq] , g[q] , sizeof(g[nq]));
      fail[nq][0] = fail[q][0]; fail[q][0] = nq;
      while(p && g[p][nx] == q){g[p][nx] = nq; p=fail[p][0];}
    }
    return ;
  }
  p=last; np = ++tot; step[np] = step[p] + 1; if(id) link(rt[np],1,n,id);
  while(p && !g[p][nx]){g[p][nx] = np; p=fail[p][0];}
  if(!p) fail[np][0] = 1;
  else
  {
    q = g[p][nx];
    if(step[q] == step[p]+1) fail[np][0] = q;
    else
    {
      nq = ++tot; step[nq] = step[p] + 1;
      memcpy(g[nq] , g[q] , sizeof(g[nq]));
      fail[nq][0] = fail[q][0]; fail[np][0] = fail[q][0] = nq;
      while(p && g[p][nx] == q){g[p][nx] = nq; p=fail[p][0];}
    }
  }last = np;
}

char str[N]; int R[N]; int rk[N];


int main()
{

  last=tot=1;

  scanf("%s",ss+1); int len = strlen(ss+1);
  for(int i=1;i<=len;i++)
  {
    insert(ss[i]-'a'+1,0);
    pos[i] = last;
  }

  n = read();

  for(int i=1;i<=n;i++)
  {
    scanf("%s",str+1); int l = strlen(str+1);
    last = 1; for(int j=1;j<=l;j++) insert(str[j]-'a'+1,i);
  }

  for(int j=1;j<=20;j++) for(int i=1;i<=tot;i++) fail[i][j] = fail[fail[i][j-1]][j-1];

  for(int i=1;i<=tot;i++) R[step[i]]++;
  for(int i=1;i<=tot;i++) R[i] += R[i-1];
  for(int i=tot;i>=1;i--) rk[R[step[i]]--] = i;

  for(int i=tot;i>=1;i--)
  {
    int x=rk[i];
    if(fail[x][0]) merge(rt[fail[x][0]],rt[x],1,n);
  }

  int q = read();
  while(q--)
  {
    int a = read(); int b = read(); int l = read(); int r = read();
    int u = pos[r];
    for(int j=20;j>=0;j--) if(step[fail[u][j]] >= r-l+1) u = fail[u][j];
    mx = qry(rt[u],1,n,a,b);
    idd = qry2(rt[u],1,n,a,b);
    printf("%d %d\n",idd,mx);

  }

  return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_39708759/article/details/80116163