bzoj 2555 SubString(SAM+LCT)
【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=2555
【题意】
给定一个字符串,可以随时插入字符串,提供查询s在其中作为连续子串的出现次数。
【思路】
子串的出现次数,这使我们想到了后缀自动机,如果没有插入操作,则出现次数为字符串对应节点|right|集的大小。
Right的递推方法为:|fa->right| <- |right|
如果暴力做的话,可以每一次插入都重新计算right。时间复杂度为O(mn)。
因为需要不断地插入字符串,所以parent树会发生变化,我们考虑使用LCT维护parent树。SAM中插入字符串时需要改变父亲,对应到LCT中即切断原树中本来的父亲,连接新的父亲,最后还应该把np的所有fa的|right|加1,对应于LCT中的一次区间加值。
这样只需要用LCT维护一个值与一个懒标记即可。
注意LCT原来的Link是连接两个点,而这里的Link需要切断原来的父亲后连接新父亲。
【代码】
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define FOR(a,b,c) for(int a=b;a<=c;a++) 5 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt) 6 using namespace std; 7 8 const int N = 4e6+10; 9 10 namespace LCT { 11 12 struct Node { 13 Node *ch[2],*fa; 14 int v,add,rev; 15 Node() ; 16 void addv(int x) { 17 v=v+x; 18 add=add+x; 19 } 20 void reverse() { 21 rev^=1; 22 swap(ch[0],ch[1]); 23 } 24 void up_push() { 25 if(fa->ch[0]==this||fa->ch[1]==this) 26 fa->up_push(); 27 if(add) { 28 ch[0]->addv(add); 29 ch[1]->addv(add); 30 add=0; 31 } 32 if(rev) { 33 ch[0]->reverse(); 34 ch[1]->reverse(); 35 rev=0; 36 } 37 } 38 void maintain() { 39 } 40 } *null=new Node ; 41 Node:: Node() { 42 fa=ch[0]=ch[1]=null; 43 add=v=rev=0; 44 } 45 46 void rot(Node* o,int d) { 47 Node *p=o->fa; 48 p->ch[d]=o->ch[d^1]; 49 o->ch[d^1]->fa=p; 50 o->ch[d^1]=p; 51 o->fa=p->fa; 52 if(p==p->fa->ch[0]) 53 p->fa->ch[0]=o; 54 else if(p==p->fa->ch[1]) 55 p->fa->ch[1]=o; 56 p->fa=o; 57 p->maintain(); 58 } 59 void splay(Node* o) { 60 o->up_push(); 61 Node *nf,*nff; 62 while(o->fa->ch[0]==o||o->fa->ch[1]==o) { 63 nf=o->fa,nff=nf->fa; 64 if(o==nf->ch[0]) { 65 if(nf==nff->ch[0]) rot(nf,0); 66 rot(o,0); 67 } else { 68 if(nf==nff->ch[1]) rot(nf,1); 69 rot(o,1); 70 } 71 } 72 o->maintain(); 73 } 74 void Access(Node* o) { 75 Node* son=null; 76 while(o!=null) { 77 splay(o); 78 o->ch[1]=son; 79 o->maintain(); 80 son=o; o=o->fa; 81 } 82 } 83 void evert(Node* o) { 84 Access(o); 85 splay(o); 86 o->reverse(); 87 } 88 /* 89 void Link(Node *u,Node *v) { 90 evert(u); 91 u->fa=v; 92 } 93 void Cut(Node *u,Node *v) { 94 evert(u); 95 Access(v); splay(v); 96 u->fa=v->ch[0]=null; 97 v->maintain(); 98 } 99 */ 100 void Link(Node* u,Node* v) { //改变父亲为v 需要切断原来的父亲 101 Access(u),splay(u); 102 u->ch[0]->fa=u->ch[0]=null; 103 u->fa=v; 104 } 105 106 } 107 108 namespace SAM { 109 110 struct Snode { 111 Snode *ch[26],*fa; 112 int l; 113 LCT::Node *lct; 114 Snode(int _=0) :fa(0x0),l(_) { 115 memset(ch,0,sizeof(ch)); 116 lct=new LCT::Node; 117 } 118 } *root=new Snode,*last=root; 119 120 void add(int x) { 121 Snode *p=last,*np=new Snode(p->l+1); 122 last=np; 123 for(;p&&!p->ch[x];p=p->fa) 124 p->ch[x]=np; 125 if(!p) { 126 np->fa=root; 127 LCT::Link(np->lct,root->lct); 128 } else { 129 Snode *q=p->ch[x]; 130 if(q->l==p->l+1) { 131 np->fa=q; 132 LCT::Link(np->lct,q->lct); 133 } else { 134 Snode* nq=new Snode(p->l+1); 135 memcpy(nq->ch,q->ch,sizeof nq->ch); 136 nq->fa=q->fa; 137 LCT::Link(nq->lct,q->fa->lct); 138 np->fa=nq; q->fa=nq; 139 LCT::Link(q->lct,nq->lct); //修改parent树中的父亲 140 LCT::Link(np->lct,nq->lct); 141 q->lct->up_push(); 142 nq->lct->v=q->lct->v; 143 144 for(;p&&p->ch[x]==q;p=p->fa) 145 p->ch[x]=nq; 146 } 147 } 148 LCT::Access(np->lct); 149 LCT::splay(np->lct); 150 np->lct->addv(1); 151 } 152 void insert(char *s) { 153 for(int i=0;s[i];i++) 154 add(s[i]-'A'); 155 } 156 int query(char *s) { 157 for(Snode *p=root;p;p=p->ch[(*s++)-'A']) 158 if(!*s) return p->lct->up_push(),p->lct->v; 159 return 0; 160 } 161 162 } 163 164 void Decode(char s[],int mask) 165 { 166 int i,n=strlen(s); 167 for(int i=0;i<n;i++) { 168 mask=(mask*131+i)%n; 169 swap(s[i],s[mask]); 170 } 171 } 172 173 int q,mask; 174 char s[N],op[20]; 175 176 int main() 177 { 178 scanf("%d%s",&q,s); 179 SAM::insert(s); 180 while(q--) { 181 scanf("%s%s",op,s); 182 Decode(s,mask); 183 if(op[0]=='Q') { 184 int ans=SAM::query(s); 185 mask^=ans; 186 printf("%d\n",ans); 187 } else { 188 SAM::insert(s); 189 } 190 } 191 return 0; 192 }
posted on 2016-03-26 14:39 hahalidaxin 阅读(444) 评论(0) 编辑 收藏 举报