E. Forensic Examination

http://codeforces.com/problemset/problem/666/E

题目大意:给模式串S以及m个特殊串,q个询问,询问S的子串[pl,pr]在特殊串编号属于[l,r]中出现次数最多的次数以及在哪个特殊串。

一开始打算用S建SAM,特殊串去匹配。。。测样例时才想起这样不对。胡搞一番后,才开始写下面的做法。

S与特殊串连在一起建SAM,记录S串[1,x]在SAM上节点位置,用来子串定位。每个节点的{right}该串出现的所有地方。于是题目成了求一个节点的{right}中属于某个特殊串次数最多的特殊串。给这些{right}染色,即标记属于哪个特殊串。一个节点用一棵线段树维护最值,然后用线段树合并求出每个节点的线段树。

复杂度O(nlogn)。。。这n大概为106

  1. #include<cstdio>
  2. #include<cmath>
  3. #include<cstring>
  4. const int len(),lem(),N();
  5. struct Node{int pre,nx[],step;}sam[N*+];
  6. int top=,now=,root=,last,lastson;int n,pos[len+];
  7. int m,q,leng,l,r,pl,pr;char str[len+];
  8. int f[][N*+],logg;
  9. struct SegMent{int nx[];unsigned short int big,pos;}tree[lem+];
  10. int stot,rt[N*+];
  11. struct data{unsigned short int x,y;};
  12. void extend(int x)
  13. {
  14. last=now;now=++top;sam[now].step=sam[last].step+;
  15. for(;!sam[last].nx[x]&&last;last=sam[last].pre)
  16. sam[last].nx[x]=now;
  17. if(!last)sam[now].pre=root;
  18. else
  19. {
  20. lastson=sam[last].nx[x];
  21. if(sam[lastson].step==sam[last].step+)sam[now].pre=lastson;
  22. else
  23. {
  24. sam[++top]=sam[lastson];sam[top].step=sam[last].step+;
  25. sam[now].pre=sam[lastson].pre=top;
  26. for(;sam[last].nx[x]==lastson&&last;last=sam[last].pre)
  27. sam[last].nx[x]=top;
  28. }
  29. }
  30. }
  31. void update(int k)
  32. {
  33. int next;
  34. if(tree[tree[k].nx[]].big>tree[tree[k].nx[]].big||
  35. (tree[tree[k].nx[]].big==tree[tree[k].nx[]].big&&
  36. tree[tree[k].nx[]].pos<tree[tree[k].nx[]].pos))next=;
  37. else next=;
  38. tree[k].big=tree[tree[k].nx[next]].big;
  39. tree[k].pos=tree[tree[k].nx[next]].pos;
  40. }
  41. void insert(int &k,int l,int r,int x)
  42. {
  43. if(!k)k=++stot;
  44. if(l==r){tree[k].big++;tree[k].pos=x;return;}
  45. int mid=(l+r)>>;
  46. if(x<=mid)insert(tree[k].nx[],l,mid,x);
  47. else insert(tree[k].nx[],mid+,r,x);
  48. update(k);
  49. }
  50. int find(int l,int r)
  51. {
  52. int x=pos[r];
  53. for(int j=logg;j>=;j--)
  54. if(sam[f[j][x]].step>=r-l+)x=f[j][x];
  55. return x;
  56. }
  57. int merge(int x,int y,int l,int r)
  58. {
  59. if(!x||!y)return x|y;
  60. int z=++stot;
  61. if(l==r){tree[z].big=tree[x].big+tree[y].big;tree[z].pos=tree[x].pos;return z;}
  62. int mid=(l+r)>>;
  63. tree[z].nx[]=merge(tree[x].nx[],tree[y].nx[],l,mid);
  64. tree[z].nx[]=merge(tree[x].nx[],tree[y].nx[],mid+,r);
  65. update(z);
  66. return z;
  67. }
  68. int cnt[N*+],p[N*+];
  69. void update()
  70. {
  71. for(int i=;i<=top;i++)cnt[sam[i].step]++;
  72. for(int i=;i<=top;i++)cnt[i]+=cnt[i-];
  73. for(int i=top;i>=;i--)p[cnt[sam[i].step]--]=i;
  74. for(int i=top;i>=;i--)
  75. rt[sam[p[i]].pre]=merge(rt[sam[p[i]].pre],rt[p[i]],,m);
  76. }
  77. void deal()
  78. {
  79. logg=log(top)/log();
  80. for(int i=;i<=top;i++)f[][i]=sam[i].pre;
  81. for(int j=;j<=logg;j++)
  82. for(int i=;i<=top;i++)
  83. f[j][i]=f[j-][f[j-][i]];
  84. }
  85. data query(int k,int l,int r,int L,int R)
  86. {
  87. if(!k)return (data){,l};
  88. if(L==l&&R==r)return (data){tree[k].big,tree[k].pos};
  89. int mid=(L+R)>>;
  90. if(r<=mid)return query(tree[k].nx[],l,r,L,mid);
  91. else if(mid<l)return query(tree[k].nx[],l,r,mid+,R);
  92. else
  93. {
  94. data t1=query(tree[k].nx[],l,mid,L,mid),t2=query(tree[k].nx[],mid+,r,mid+,R);
  95. if(t1.x>t2.x||(t1.x==t2.x&&t1.y<t2.y))return t1;
  96. else return t2;
  97. }
  98. }
  99. void read(int &x)
  100. {
  101. x=;char ch=getchar();
  102. while(ch<''||ch>'')ch=getchar();
  103. while(ch>=''&&ch<='')x=x*+ch-'',ch=getchar();
  104. }
  105. int main()
  106. {
  107. freopen("C.in","r",stdin);
  108. freopen("C2.out","w",stdout);
  109. scanf("%s",str);
  110. n=strlen(str);
  111. for(int i=;i<n;i++)extend(str[i]-'a'),pos[i+]=now;
  112. extend();
  113. scanf("%d",&m);
  114. for(int i=;i<=m;i++)
  115. {
  116. scanf("%s",str);leng=strlen(str);
  117. for(int j=;j<leng;j++)extend(str[j]-'a'),insert(rt[now],,m,i);
  118. extend();
  119. }
  120. deal();update();
  121. scanf("%d",&q);
  122. for(int i=;i<=q;i++)
  123. {
  124. scanf("%d%d%d%d",&l,&r,&pl,&pr);
  125. int x=find(pl,pr);data tmp;
  126. tmp=query(rt[x],l,r,,m);
  127. if(tmp.x==)tmp.y=l;
  128. printf("%d %d\n",tmp.y,tmp.x);
  129. }
  130. return ;
  131. }

第二种写法:

将串连在一起求SA,设L为小于rank[pl]的第一个height[L]=pr-pl+1,R为大于rank[pl]的第一个height[R]=pr-pl+1。那么[pl,pr]能匹配的后缀区间即[L,R]。给后缀标记一下属于哪个特殊串,将题目变成了求区间众数。有趣的是这些区间不是包含关系就是相离关系。
令height[i]=(i-1,i)的(无向)边权,那么一次询问就是询问点rank[pl]只经过边权大于等于(pr-pl+1)的边所能到达点中出现次数最多的特殊串。
于是变成B3545Peaks加强版,用线段树维护,线段树合并实现。

O(nlogn)

  1. #include<cstdio>
  2. #include<cmath>
  3. #include<cstring>
  4. #include<vector>
  5. const int limt(),len(),lem();
  6. int tmp[len+],cnt[len+],p[len+];
  7. int rank[len*+],sfa[len+],height[len+];
  8. int str[len+],color[len+],val[len*+];
  9. int n,m,Q,leng,l,r,pl,pr,x;char ch[len+];
  10. struct Node{int nd,nx,co;}bot[len*+];int tot,first[len*+];
  11. int g[][len*+],f[][len*+],logg;
  12. int father[len*+],now,last;
  13. std::vector<int>weight[len+];
  14. struct ANS
  15. {
  16. unsigned short int big,pos;
  17. inline ANS operator +(const ANS&A)const{return (ANS){big+A.big,pos};}
  18. inline ANS operator *(const ANS&A)const{if(A.big<big||(big==A.big&&pos<A.pos))return (ANS){big,pos};return A;}
  19. }ans;
  20. struct SegMent{int nx[];ANS key;}tree[lem+];int stot,root[len*+];
  21. void read(int &x)
  22. {
  23. x=;int f=;char ch=getchar();
  24. while((ch<''||ch>'')&&(ch!='-')){ch=getchar();}if(ch=='-')f=-,ch=getchar();
  25. while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
  26. if(f<)x=-x;
  27. }
  28. void add(int a,int b,int c){bot[++tot]=(Node){b,first[a],c};first[a]=tot;}
  29. void swap(int &a,int &b){if(a==b)return;a^=b;b^=a;a^=b;}
  30. int min(int a,int b){return a>b?b:a;}
  31.  
  32. bool com(int x,int y,int l){return rank[x]==rank[y]&&rank[x+l]==rank[y+l];}
  33. void doubling()
  34. {
  35. for(int i=;i<=n;i++)rank[i]=str[i],sfa[i]=i;
  36. for(int pos=,l=,sigma=limt;pos<n;sigma=pos)
  37. {
  38. pos=;
  39. for(int i=n-l+;i<=n;i++)p[++pos]=i;
  40. for(int i=;i<=n;i++)if(sfa[i]>l)p[++pos]=sfa[i]-l;
  41. memset(cnt,,sizeof(int)*(sigma+));pos=;
  42. for(int i=;i<=n;i++)cnt[rank[i]]++;
  43. for(int i=;i<=sigma;i++)cnt[i]+=cnt[i-];
  44. for(int i=n;i>=;i--)sfa[cnt[rank[p[i]]]--]=p[i];
  45. for(int i=;i<=n;i++)tmp[sfa[i]]=com(sfa[i],sfa[i-],l)?pos:++pos;
  46. for(int i=;i<=n;i++)rank[i]=tmp[i];
  47. l=!l?:l<<;
  48. }
  49. for(int i=;i<=n;i++)rank[sfa[i]]=i;
  50. for(int i=,j,k;i<=n;i++)
  51. {
  52. k=sfa[rank[i]-]; if(!k)continue;
  53. j=height[rank[i-]]; if(j)j--;
  54. while(str[i+j]==str[k+j])
  55. j++;
  56. height[rank[i]]=j;
  57. }
  58. }
  59. int gf(int x)
  60. {
  61. int v=x,o;while(v!=father[v])v=father[v];
  62. for(;x!=father[x];x=o){o=father[x];father[x]=v;}
  63. return v;
  64. }
  65. void exchange()
  66. {
  67. for(int i=;i<=n;i++)weight[height[i]].push_back(i);
  68. for(int i=;i<=n;i++)val[i]=color[sfa[i]];
  69. for(int i=;i<=n*;i++)father[i]=i; now=n;last=now;
  70. for(int i=n,fa,fb;i>=;i--)
  71. {
  72. last=now;
  73. for(int v=;v<(int)weight[i].size();v++)
  74. {
  75. int t=weight[i][v];
  76. fa=gf(weight[i][v]-);fb=gf(weight[i][v]);
  77. if(fa!=fb)
  78. {
  79. now++;
  80. father[fb]=father[fa]=father[now];
  81. if(now!=fb)add(now,fb,i);
  82. if(now!=fa)add(now,fa,i);
  83. }
  84. }
  85. }
  86. now++;
  87. for(int i=,fa;i<=n;i++)
  88. {
  89. fa=gf(i);
  90. if(fa!=now)add(now,fa,);father[fa]=now;
  91. }
  92. }
  93. void update(int k){tree[k].key=tree[tree[k].nx[]].key*tree[tree[k].nx[]].key;}
  94. void insert(int &k,int l,int r,int x)
  95. {
  96. if(!k)k=++stot;
  97. if(l==r){tree[k].key.big++;tree[k].key.pos=l;return;}
  98. int mid=(l+r)>>;
  99. if(x<=mid)insert(tree[k].nx[],l,mid,x);
  100. else insert(tree[k].nx[],mid+,r,x);
  101. update(k);
  102. }
  103. int merge(int x,int y,int l,int r)
  104. {
  105. if(!x||!y)return (x|y);
  106. int z=++stot,mid=(l+r)>>;
  107. if(l==r){tree[z].key=tree[x].key+tree[y].key;return z;}
  108. tree[z].nx[]=merge(tree[x].nx[],tree[y].nx[],l,mid);
  109. tree[z].nx[]=merge(tree[x].nx[],tree[y].nx[],mid+,r);
  110. update(z);
  111. return z;
  112. }
  113. void dfs(int x)
  114. {
  115. if(val[x])insert(root[x],,m,val[x]);
  116. for(int v=first[x];v;v=bot[v].nx)
  117. {
  118. dfs(bot[v].nd); f[][bot[v].nd]=x; g[][bot[v].nd]=bot[v].co;
  119. root[x]=merge(root[x],root[bot[v].nd],,m);
  120. // fprintf(stdout,"%d\n",stot);
  121. }
  122. }
  123. void Fdeal()
  124. {
  125. logg=log(now)/log();
  126. for(int j=;j<=logg;j++)
  127. for(int i=;i<=now;i++)
  128. f[j][i]=f[j-][f[j-][i]],g[j][i]=min(g[j-][i],g[j-][f[j-][i]]);
  129. }
  130. int find(int pl,int pr)
  131. {
  132. int x=rank[pl];
  133. for(int j=logg;j>=;j--)
  134. if(g[j][x]>=pr-pl+)x=f[j][x];
  135. return x;
  136. }
  137. ANS query(int k,int L,int R,int l,int r)
  138. {
  139. if(!k)return (ANS){,l};
  140. if(l==L&&r==R)return tree[k].key;
  141. int mid=(L+R)>>;
  142. if(r<=mid)return query(tree[k].nx[],L,mid,l,r);
  143. else if(mid<l)return query(tree[k].nx[],mid+,R,l,r);
  144. else return query(tree[k].nx[],L,mid,l,mid)*query(tree[k].nx[],mid+,R,mid+,r);
  145. }
  146. int main()
  147. {
  148. // freopen("C.in","r",stdin);
  149. // freopen("C.out","w",stdout);
  150. scanf("%s",ch);
  151. leng=strlen(ch);
  152. for(int i=;i<leng;i++)str[++n]=ch[i]-'a'+;
  153. str[++n]=; scanf("%d",&m);
  154. for(int i=;i<=m;i++)
  155. {
  156. scanf("%s",ch);
  157. leng=strlen(ch);
  158. for(int j=;j<leng;j++)str[++n]=ch[j]-'a'+,color[n]=i;
  159. str[++n]=;
  160. }
  161. doubling();
  162. exchange();//将序列变成树
  163. dfs(now);
  164. Fdeal();
  165. scanf("%d",&Q);
  166. for(int i=;i<=Q;i++)
  167. {
  168. read(l);read(r);read(pl);read(pr);
  169. x=find(pl,pr);
  170. ans=query(root[x],,m,l,r);
  171. printf("%d %d\n",ans.pos,ans.big);
  172. }
  173. return ;
  174. }

Codeforces 666E Forensic Examination SAM or SA+线段树合并的更多相关文章

  1. Codeforces 666E Forensic Examination SAM+权值线段树

    第一次做这种$SAM$带权值线段树合并的题 然而$zjq$神犇看完题一顿狂码就做出来了 $Orz$ 首先把所有串当成一个串建$SAM$ 我们对$SAM$上每个点 建一棵权值线段树 每个叶子节点表示一个 ...

  2. Codeforces.666E.Forensic Examination(广义后缀自动机 线段树合并)

    题目链接 \(Description\) 给定串\(S\)和\(m\)个串\(T_i\).\(Q\)次询问,每次询问\(l,r,p_l,p_r\),求\(S[p_l\sim p_r]\)在\(T_l\ ...

  3. CF 666E Forensic Examination——广义后缀自动机+线段树合并

    题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ...

  4. CF666E Forensic Examination SAM+倍增,线段树和并

    题面: 给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[p_l..p_r]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数.如有多解输出最靠前的那一个. 分析: 第 ...

  5. CF666E Forensic Examination [后缀自动机,线段树合并]

    洛谷 Codeforces 思路 最初的想法:后缀数组+区间众数,似乎并不能过. 既然后缀数组不行,那就按照套路建出广义SAM,然后把\(S\)放在上面跑,得到以每个点结尾会到SAM上哪个节点. 询问 ...

  6. cf666E. Forensic Examination(广义后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...

  7. 【CF666E】Forensic Examination - 广义后缀自动机+线段树合并

    广义SAM专题的最后一题了……呼 题意: 给出一个长度为$n$的串$S$和$m$个串$T_{1\cdots m}$,给出$q$个询问$l,r,pl,pr$,询问$S[pl\cdots pr]$在$T_ ...

  8. CF666E Forensic Examination(后缀自动机+线段树合并)

    给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[pl..pr]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 我们首先对m个字符串 ...

  9. [CF666E]Forensic Examination:后缀自动机+线段树合并

    分析 用到了两个小套路: 使用线段树合并维护广义后缀自动机的\(right\)集合. 查询\(S[L,R]\)在\(T\)中的出现次数:给\(T\)建SAM,在上面跑\(S\),跑到\(R\)的时候先 ...

随机推荐

  1. qt5.3+vs2013乱码

    解决qt5.3+vs2013乱码,在main函数之前加入 #if _MSC_VER >= 1600 #pragma execution_character_set("utf-8&quo ...

  2. C#Timer停不住

    System.Timers.Timer timer1 = new System.Timers.Timer(); timer1.Interval = ; //1天循环一次 timer1.Elapsed ...

  3. 汇总各个部门当前员工的title类型的分配数目,结果给出部门编号dept_no、dept_name、其当前员工所有的title以及该类型title对应的数目count

    CREATE TABLE `departments` (`dept_no` char(4) NOT NULL,`dept_name` varchar(40) NOT NULL,PRIMARY KEY ...

  4. jquery.jscrollpane.js滚动速度设置

    首先找到插件里面的这个函数,改变成下面的样子: function initMousewheel() { container.unbind(mwEvent).bind( mwEvent, functio ...

  5. linux查看系统版本(适用于centos、ubutun,其他类型没有进行测试)

    方法一:cat /etc/issue 或more /etc/issue root@salt-master:~# cat /etc/issueUbuntu 16.04.2 LTS \n \l 方法二:l ...

  6. HDU-1068-GirlsandBoys(最大独立集,二分图匹配)

    链接:https://vjudge.net/problem/HDU-1068#author=0 题意: 学校对n个学生(男女都有)进行的调查了,发现了某些学生暗生情愫,现在需要你选出一个最大的集合,这 ...

  7. sesstionStorage和localStorage

    使用: 对于多页面的pc端,为了同步多页面的消息提醒,可以将数据储存在localStorage中,多页面共享同一个localStorage.然后使用setInterval轮询获取数据,执行逻辑代码 s ...

  8. redis 拒绝远程访问解决

    启动时报的警告: 1.Warning: no config file specified, using the default config. In order to specify a config ...

  9. AWR实战分析之----direct path read temp

    http://blog.sina.com.cn/s/blog_61cd89f60102eej1.html 1.direct path read temp select TOTAL_BLOCKS,USE ...

  10. linux批量替换指定文件夹中所有文件的指定内容

    命令:sed -i "s#https#http#g" `grep http -rl VEROMODA` 功能:用来替换当前目录VEROMODA文件夹及子文件夹中所有文件中的http ...