题目大意:

懒得概括了

神题,搞了2个半晚上,还认为自己的是对的...一直调不过,最后终于在jdr神犇的帮助下过了这道题

线段树合并该是这道题最好理解且最好写的做法了,貌似主席树也行?但线段树合并这个算法实在是太优美了

一个模式串从左到右为开头进行匹配,如果在前面已经匹配成功了,后面就算能匹配成功也没用

因此在$parent$树里维护一个数组$mi_{x}$,表示在$parent$树中,节点$x$的子树中$len_{x}$的最小值,可以用桶+拓扑$O(n)$实现

如果一个模式串$T$是$S$的一个子串

首先用上面维护的$mi_{x}$数组找出这个串能被匹配上的,第一个末尾位置$pos$

显然,以$[1,pos-len]$为开头,向后进行暴力匹配,都匹配不出$T$,每个位置为开头都失配一次,失配的总长度是$pos-len$

接下来就是解决以$[1,pos-len]$为开头,能匹配上$T$的一小部分前缀的情况了

直接讨论每个位置最多能往后匹配多长,会很复杂(如果大家想看这种做法可以看大师的博客)

转化问题

我们讨论$T$的每个前缀,在$S$一定范围内的前缀中,作为后缀出现几次不就行了

我们把$T$串放到$trs$图里跑

现在走到了一个节点$x$,已经走过的路径长度是$i$,它的$right$集合可以用线段树合并预处理出来,我们只需要求出$x$的$parent$子树内,$len$小于某个上限的$endpos$节点数量就行了

推导可得,这个上限是$pos-len+i$,因为再往后就会超出第一次匹配的位置,不可行

如果$T$不是$S$的一个子串,失配长度就是$n$,上限也全都改成$n$就行了

而且$endpos$节点的$len$互不相同,恰好契合了线段树合并的性质,预处理的时候从叶节点一直往上合并即可

  1. #include <cmath>
  2. #include <vector>
  3. #include <cstdio>
  4. #include <cstring>
  5. #include <algorithm>
  6. #define N1 105000
  7. #define S1 (N1<<1)
  8. #define T1 (N1<<2)
  9. #define M1 105000
  10. #define ll long long
  11. #define uint unsigned int
  12. #define rint register int
  13. #define dd double
  14. #define il inline
  15. #define inf 0x3f3f3f3f
  16. #define idx(X) (X-'0')
  17. using namespace std;
  18.  
  19. int gint()
  20. {
  21. int ret=,fh=;char c=getchar();
  22. while(c<''||c>''){if(c=='-')fh=-;c=getchar();}
  23. while(c>=''&&c<=''){ret=ret*+c-'';c=getchar();}
  24. return ret*fh;
  25. }
  26. int n,m,len,de;
  27. char str[N1];
  28. struct Edge{
  29. int to[S1],nxt[S1],head[S1],cte;
  30. void ae(int u,int v){
  31. cte++;to[cte]=v,nxt[cte]=head[u],head[u]=cte;}
  32. }E;
  33. namespace seg{
  34. int ls[S1*],rs[S1*],root[S1],tot;
  35. ll sum[S1*];
  36. int merge(int rt1,int rt2)
  37. {
  38. if(!rt1||!rt2) return rt1+rt2;
  39. int nx=++tot;
  40. sum[nx]=sum[rt1]+sum[rt2];
  41. ls[nx]=merge(ls[rt1],ls[rt2]);
  42. rs[nx]=merge(rs[rt1],rs[rt2]);
  43. return nx;
  44. }
  45. void update(int x,int l,int r,int &rt)
  46. {
  47. if(!rt) rt=++tot;
  48. sum[rt]=;
  49. if(l==r) return;
  50. int mid=(l+r)>>;
  51. if(x<=mid) update(x,l,mid,ls[rt]);
  52. else update(x,mid+,r,rs[rt]);
  53. //pushup(rt);
  54. }
  55. ll query(int L,int R,int l,int r,int rt)
  56. {
  57. if(!rt) return ;
  58. if(L<=l&&r<=R) return sum[rt];
  59. int mid=(l+r)>>;ll ans=;
  60. if(L<=mid) ans+=query(L,R,l,mid,ls[rt]);
  61. if(R>mid) ans+=query(L,R,mid+,r,rs[rt]);
  62. return ans;
  63. }
  64. };
  65. namespace SAM{
  66. int trs[S1][],pre[S1],dep[S1],ed[S1],mi[S1],la,tot;
  67. void init(){tot=la=;}
  68. void insert(int c,int id)
  69. {
  70. int p=la,np=++tot,q,nq;la=np;
  71. dep[np]=dep[p]+;ed[np]=id;
  72. for(;p&&!trs[p][c];p=pre[p]) trs[p][c]=np;
  73. seg::update(id,,n,seg::root[np]);
  74. if(!p){pre[np]=;return;}
  75. q=trs[p][c];
  76. if(dep[q]==dep[p]+) pre[np]=q;
  77. else{
  78. pre[nq=++tot]=pre[q];
  79. pre[q]=pre[np]=nq;
  80. dep[nq]=dep[p]+;
  81. memcpy(trs[nq],trs[q],sizeof(trs[q]));
  82. for(;p&&trs[p][c]==q;p=pre[p]) trs[p][c]=nq;
  83. }
  84. }
  85. int hs[S1],que[S1],edq[S1],k,l,tt;
  86. void build()
  87. {
  88. //memset(mi,0x3f,sizeof(mi));
  89. for(int i=;i<=tot;i++) mi[i]=n+;
  90. for(int i=;i<=tot;i++) hs[dep[i]]++;
  91. for(int i=;i<=n;i++) hs[i]+=hs[i-];
  92. for(int i=;i<=tot;i++) que[hs[dep[i]]--]=i;
  93. for(int i=tot-,x;i>=;i--)
  94. {
  95. x=que[i],E.ae(pre[x],x);
  96. if(ed[x]) mi[x]=min(mi[x],ed[x]);
  97. mi[pre[x]]=min(mi[pre[x]],mi[x]);
  98. seg::root[pre[x]]=seg::merge(seg::root[pre[x]],seg::root[x]);
  99. }
  100. }
  101. void find(int L,int &F)
  102. {
  103. int x=,c,fl=;
  104. for(int i=;i<=L;i++)
  105. {
  106. c=idx(str[i]);
  107. //for(;x&&!trs[x][c];x=pre[x]);
  108. if(!trs[x][c]) return;
  109. x=trs[x][c];
  110. }
  111. F=mi[x];
  112. }
  113. };
  114.  
  115. int main()
  116. {
  117. scanf("%d",&n);
  118. scanf("%s",str+);
  119. SAM::init();
  120. for(int i=;i<=n;i++)
  121. SAM::insert(idx(str[i]),i);
  122. SAM::build();
  123. scanf("%d",&m);
  124. int F,c,x;
  125. for(int i=;i<=m;i++)
  126. {
  127. scanf("%s",str+);
  128. len=strlen(str+);
  129. F=n+;
  130. SAM::find(len,F);
  131. ll ans=;
  132. if(F!=n+) ans=F-len;
  133. else ans=n;
  134. x=;
  135. for(int j=;j<=len;j++)
  136. {
  137. c=idx(str[j]);
  138. //for(;x&&!SAM::trs[x][c];x=SAM::pre[x]);
  139. x=SAM::trs[x][c];
  140. if(!x) break;
  141. ans+=seg::query(,(F==n+?n:F-len+j),,n,seg::root[x]);
  142. }
  143. printf("%lld\n",ans);
  144. }
  145. return ;
  146. }

BZOJ 3413 匹配 (后缀自动机+线段树合并)的更多相关文章

  1. BZOJ3413: 匹配(后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...

  2. bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)

    bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...

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

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

  4. [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)

    https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...

  5. 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)

    模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...

  6. 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)

    点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...

  7. BZOJ.5417.[NOI2018]你的名字(后缀自动机 线段树合并)

    LOJ 洛谷 BZOJ 考虑\(l=1,r=|S|\)的情况: 对\(S\)串建SAM,\(T\)在上面匹配,可以得到每个位置\(i\)的后缀的最长匹配长度\(mx[i]\). 因为要去重,对\(T\ ...

  8. BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并

    题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...

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

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

随机推荐

  1. 01.Python基础-5.函数

    1 函数的介绍 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 内置函数 自定义函数 2 函数的定义和调用 2.1 函数的定义和调用 定义 def 函数名([参数]): 代码块 [ ...

  2. 使用maven服务器插件 运行项目

    使用jetty插件  部署运行 创建一个maven项目:去Maven仓库中寻找jetty插件  然后复制到pom.xml中 使用命令  运行程序: 然后控制台打印: 通过浏览器   访问: ----- ...

  3. LinkedList 注意事项

      public E getFirst() 返回此列表的第一个元素. public E getLast() 返回此列表的最后一个元素. public E removeFirst() 移除并返回此列表的 ...

  4. 洛谷 1775. [国家集训队2010]小Z的袜子

    1775. [国家集训队2010]小Z的袜子 ★★★   输入文件:hose.in   输出文件:hose.out   简单对比时间限制:1 s   内存限制:512 MB [题目描述] 作为一个生活 ...

  5. lpad&amp;rpad

    lpad( string, padded_length, [ pad_string ] ) string: 准备被填充的字符串 padded_length: 填充之后的字符串长度 pad_string ...

  6. wpf获取目录路径

    AppDomain.CurrentDomain.BaseDirectory +文件名即可,简单吧? //获取启动了应用程序的可执行文件的路径,不包括可执行文件的名称. string str5=Appl ...

  7. Rails中关联数据表的添加操作(嵌套表单)

    很早就听说有Web敏捷开发这回事,最近终于闲了下来,可以利用业余的时间学些新东西,入眼的第一个东东自然是Ruby on Rails.Rails中的核心要素也就是MVC.ORM这些了,因此关于Rails ...

  8. 终端安全工具 gartner 排名

    Reviews for Endpoint Detection and Response Solutions What is Endpoint Detection and Response Soluti ...

  9. 最小生成树基础 (Kruskal)

    最小生成树 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status ...

  10. python面向对象与结构成员之间的关系

    1面向对象结构分析:----面向对象整体大致分为两块区域:-------第一部分:静态字段(静态变量)部分-------第二部分:方法部分--每个区块可以分为多个小部分 class A: countr ...