bzoj 2555 SubString —— 后缀自动机+LCT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2555

建立后缀自动机,就可以直接加入新串了;

出现次数就是 Right 集合的大小,需要查询 Parent 树上的子树和;

所以可以用 LCT 维护 Parent 树,因为 Parent 树是有根树所以不需要 makeroot;

代码中的两种 cut 写法都可以,其实这里的 splay 节点上记的 siz 值不是 splay 子树里的而是原子树( Parent 树上)里的;

注意读入的函数内不改变 mask -_-

splay 用栈时不要改变 x !

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int const xn=1200005,xm=3e6+5;
int fa[xn],lst=1,cnt=1,l[xn],go[xn][30],siz[xn];
int pre[xn],c[xn][2],sta[xn],top,lzy[xn],mask;
char dc[10],s[xm];
void turn(int x,int w){siz[x]+=w; lzy[x]+=w;}//
bool isroot(int x){return c[pre[x]][0]!=x&&c[pre[x]][1]!=x;}
void pushdown(int x)
{
  if(!lzy[x])return;
  int ls=c[x][0],rs=c[x][1];
  turn(ls,lzy[x]); turn(rs,lzy[x]);
  lzy[x]=0;
}
void rotate(int x)
{
  int y=pre[x],z=pre[y],d=(c[y][1]==x);
  if(!isroot(y))c[z][c[z][1]==y]=x;
  pre[x]=z; pre[y]=x; pre[c[x][!d]]=y;
  c[y][d]=c[x][!d]; c[x][!d]=y;
}
void splay(int x)
{
  sta[top=1]=x;
  //while(!isroot(x))sta[++top]=pre[x],x=pre[x];//don‘t change x!!
  for(int i=x;!isroot(i);i=pre[i])sta[++top]=pre[i];
  while(top)pushdown(sta[top--]);

  while(!isroot(x))
    {
      int y=pre[x],z=pre[y];
      if(!isroot(y))
    {
      if((c[y][0]==x)^(c[z][0]==y))rotate(x);
      else rotate(y);
    }
      rotate(x);
    }
}
void access(int x)
{
  for(int t=0;x;c[x][1]=t,t=x,x=pre[x])splay(x);
}
void link(int x,int f)
{
  pre[x]=f; access(f); splay(f); turn(f,siz[x]);//line to root
}
void Cut(int x)
{
  access(x); splay(x); turn(c[x][0],-siz[x]); pre[c[x][0]]=0; c[x][0]=0;//c[x][0]:parent
}
void cut(int x)//
{
  access(x); int y=fa[x]; splay(y);//
  turn(y,-siz[x]); fa[x]=0; c[y][1]=0;
}
void add(int w)
{
  int p=lst,np=++cnt; lst=np; l[np]=l[p]+1; siz[np]=1;
  for(;p&&!go[p][w];p=fa[p])go[p][w]=np;
  if(!p)fa[np]=1,link(np,1);
  else
    {
      int q=go[p][w];
      if(l[q]==l[p]+1)fa[np]=q,link(np,q);
      else
    {
      int nq=++cnt; l[nq]=l[p]+1;
      memcpy(go[nq],go[q],sizeof go[q]);
      fa[nq]=fa[q]; link(nq,fa[q]);
      fa[np]=nq; link(np,nq);
      cut(q); fa[q]=nq; link(q,nq);
      for(;go[p][w]==q;p=fa[p])go[p][w]=nq;
    }
    }
}
int query(int l)
{
  int p=1;//1
  for(int i=0;i<l;i++)
    {
      if(!go[p][s[i]-‘A‘])return 0;
      p=go[p][s[i]-‘A‘];
    }
  access(p); splay(p); return siz[p];//
}
int main()
{
  int Q; scanf("%d",&Q);
  scanf("%s",s+1); int l=strlen(s+1);
  for(int i=1;i<=l;i++)add(s[i]-‘A‘);
  for(int i=1;i<=Q;i++)
    {
      scanf("%s",dc); scanf("%s",s); l=strlen(s);
      int tmp=mask;//!!-_-
      for(int j=0;j<l;j++)
    {
      tmp=(tmp*131+j)%l;
      char t=s[j]; s[j]=s[tmp]; s[tmp]=t;
    }
      if(dc[0]==‘A‘)for(int j=0;j<l;j++)add(s[j]-‘A‘);
      else
    {
      int ans=query(l); mask^=ans;
      printf("%d\n",ans);
    }
    }
  return 0;
}

原文地址:https://www.cnblogs.com/Zinn/p/10107136.html

时间: 2024-10-11 13:10:48

bzoj 2555 SubString —— 后缀自动机+LCT的相关文章

BZOJ 2555: SubString [后缀自动机 LCT]

2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 2045  Solved: 583[Submit][Status][Discuss] Description 懒得写背景了,给你一个字符串init,要求你支持两个操作        (1):在当前字符串的后面插入一个字符串        (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)        你必须在线支持这些操作. Input 第一行一个数Q表示

bzoj 2555 SubString——后缀自动机+LCT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2555 要维护 right 集合的大小.因为 fa 会变,且 fa 构成一棵树,所以考虑用 LCT 来维护-- 和平常写的 LCT 不太一样.因为要的值是原树上子树里的值,所以没有 makeroot ,splay 里不维护 splay 里的子树信息,只维护加法标记,表示 link 一下就给原树的自己到根的那条链上的所有点加了自己的值.cut 就是减掉自己的值.所以 query 或者 spla

2555: SubString 后缀自动机+LCT

2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 688  Solved: 235[Submit][Status][Discuss] Description 懒得写背景了,给你一个字符串init,要求你支持两个操作          (1):在当前字符串的后面插入一个字符串          (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)          你必须在线支持这些操作. Input 第一行一

BZOJ 2555 SubString 后缀自动机

题目大意:给出一个字符串,支持在线在字符串后面加一个字符串,查询一个字符串在串中出现过几次. 思路:如果不想写正解的话,这个题就是后缀自动机的简单应用.正解其实是LCT+SAM,但是时间比暴力慢一倍... 暴力就很简单了,正序建立后缀自动机,每次查询的时候找到位置直接输出size的值.注意两点,一个是分裂节点的时候,size也要复制过去.查询的时候发现找不到要return 0; CODE: #include <cstdio> #include <cstring> #include

BZOJ 2555 Substring 后缀自动机+Link-Cut-Tree

题目大意:给定一个初始字符串,提供两种操作: 1.在这个字符串的后面连接一个字符串 2.询问某个字符串在当前串中出现了多少次 SAM大叔的自动机~~ 对于每个询问就是在后缀自动机上找到该子串所对应的节点 找不到返回0 然后这个节点的Right集合的大小就是这个子串的出现次数 每次Extend的时候将新建节点沿着parent指针到根的路径上所有点的Right集合大小+1即可 分裂节点的时候要将Right集合一并复制 这方法虽然暴力但是非常有效 由于parent是一棵树,所以可以用LCT来维护这棵树

bzoj 2555 SubString(SAM+LCT)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2555 [题意] 给定一个字符串,可以随时插入字符串,提供查询s在其中作为连续子串的出现次数. [思路] 子串的出现次数,这使我们想到了后缀自动机,如果没有插入操作,则出现次数为字符串对应节点|right|集的大小. Right的递推方法为:|fa->right| <- |right| 因为需要不断地插入字符串,所以parent树会发生变化,我们考虑使用LCT维护parent树.SA

【BZOJ2555】SubString 后缀自动机+LCT

[BZOJ2555]SubString Description 懒得写背景了,给你一个字符串init,要求你支持两个操作         (1):在当前字符串的后面插入一个字符串         (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)         你必须在线支持这些操作. Input 第一行一个数Q表示操作个数         第二行一个字符串表示初始字符串init         接下来Q行,每行2个字符串Type,Str         Type是ADD的话表示在

●BZOJ 2555 SubString

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2555题解: 后缀自动机+LCT 不难发现,对于输入的询问串,在自动机里trans后的到的状态的Right集合的大小就是答案. 那么后缀自动机本身就是支持在线添加的,问题就是如何维护好parent树,即如何维护好每个状态的Right集合. 那么Link-Cut-Tree就显然可以完成动态维护parent树的任务. (这里是维护的一颗根固定的树,没有Beroot()等换根函数) 代码: #in

字符串(LCT,后缀自动机):BZOJ 2555 SubString

2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1620  Solved: 471 Description 懒得写背景了,给你一个字符串init,要求你支持两个操作          (1):在当前字符串的后面插入一个字符串          (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)          你必须在线支持这些操作. Input 第一行一个数Q表示操作个数          第二行一个