后缀自动机处理多字符串字串相关问题。

首先,和后缀数组一样,用分割符连接各字符串,然后建一个后缀自动机。

我们定义一个节点代表的字符串为它原本代表的所有串去除包含分割符后的串。每个节点代表的字符串的数量可以用DP来计算(不能用right集合来算了)。

对于原来n个串中的一个串,其所有前缀可以通过将该串放到自动机上跑来获得,对于某个前缀,其所有后缀包括在该前缀本身的节点以及parent树的祖先节点中。这样我们就获得访问某个串所有子串的技能了。

对于这道题,我们可以先建出后缀自动机,然后对于n个串中的每个串,找到包含其子串的所有节点(可以保证所有子串一定且唯一出现在某个节点中)。然后将它们的计数器+1。弄完后,对于每个节点,我们就可以知道其代表的串是n个串中多少个串的子串。

最后再对于每个串,找出所有字串(不同位置要区分),统计答案。

如果不同位置不区分,那么我们得到的节点不能重复。如果要区分,对于每个前缀,其parent树上的节点都要计算,即使以前被计算过(因为他们的结束位置不同,所以肯定要计算)。

  1. /**************************************************************
  2. Problem: 3473
  3. User: idy002
  4. Language: C++
  5. Result: Accepted
  6. Time:728 ms
  7. Memory:120928 kb
  8. ****************************************************************/
  9.  
  10. #include <cstdio>
  11. #include <cstring>
  12. #include <cassert>
  13. #include <algorithm>
  14. #define N 200010
  15. #define S 500010
  16. #define P 18
  17. using namespace std;
  18.  
  19. typedef long long dnt;
  20.  
  21. int n, k;
  22. char buf[N], *shead[N];
  23. int son[S][], pnt[S], val[S], ntot, last;
  24. int head[S], dest[S], next[S], etot;
  25. int dfn[S], dep[S], anc[S][P+], idgr[S], stk[S], qu[S], top, bg, ed, idc;
  26. dnt dp[S], eff[S];
  27. int log[S];
  28.  
  29. void init() {
  30. ntot = last = ;
  31. pnt[] = -;
  32. }
  33. void append( int c ) {
  34. int p = last;
  35. int np = ++ntot;
  36. val[np] = val[p]+;
  37. while( p!=- && !son[p][c] )
  38. son[p][c]=np, p=pnt[p];
  39. if( p==- ) {
  40. pnt[np] = ;
  41. } else {
  42. int q=son[p][c];
  43. if( val[q]==val[p]+ ) {
  44. pnt[np] = q;
  45. } else {
  46. int nq = ++ntot;
  47. memcpy( son[nq], son[q], sizeof(son[nq]) );
  48. val[nq] = val[p]+;
  49. pnt[nq] = pnt[q];
  50. pnt[q] = pnt[np] = nq;
  51. while( p!=- && son[p][c]==q )
  52. son[p][c]=nq, p=pnt[p];
  53. }
  54. }
  55. last = np;
  56. }
  57. void make_topo() {
  58. for( int u=; u<=ntot; u++ ) {
  59. for( int c=; c<=; c++ ) {
  60. int v=son[u][c];
  61. if( !v ) continue;
  62. idgr[v]++;
  63. }
  64. }
  65. qu[bg=ed=] = ;
  66. while( bg<=ed ) {
  67. int u=qu[bg++];
  68. for( int c=; c<=; c++ ) {
  69. int v=son[u][c];
  70. if( !v ) continue;
  71. idgr[v]--;
  72. if( idgr[v]== )
  73. qu[++ed] = v;
  74. }
  75. }
  76. }
  77. void dodp() {
  78. make_topo();
  79. dp[] = ;
  80. for( int i=; i<=ed; i++ ) {
  81. int u=qu[i];
  82. for( int c=; c<=; c++ ) {
  83. int v=son[u][c];
  84. if( !v ) continue;
  85. dp[v] += dp[u];
  86. }
  87. }
  88. }
  89. void adde( int u, int v ) {
  90. etot++;
  91. dest[etot] = v;
  92. next[etot] = head[u];
  93. head[u] = etot;
  94. }
  95. void build() {
  96. for( int u=; u<=ntot; u++ )
  97. adde( pnt[u], u );
  98. }
  99. void dfs( int u ) {
  100. dfn[u] = ++idc;
  101. for( int p=; p<=P && anc[u][p-]; p++ )
  102. anc[u][p] = anc[anc[u][p-]][p-];
  103. for( int t=head[u]; t; t=next[t] ) {
  104. int v=dest[t];
  105. dep[v] = dep[u]+;
  106. anc[v][] = u;
  107. dfs(v);
  108. }
  109. }
  110. int lca( int u, int v ) {
  111. if( dep[u]<dep[v] ) swap(u,v);
  112. int t=dep[u]-dep[v];
  113. for( int p=; t; p++,t>>= )
  114. if( t& ) u=anc[u][p];
  115. if( u==v ) return u;
  116. for( int p=log[dep[u]]; anc[u][]!=anc[v][]; p-- )
  117. if( anc[u][p]!=anc[v][p] ) u=anc[u][p],v=anc[v][p];
  118. return anc[u][];
  119. }
  120. void fetch( char *s ) {
  121. top = ;
  122. int u = ;
  123. for( int i=; s[i]; i++ ) {
  124. int c=s[i]-'a'+;
  125. u = son[u][c];
  126. assert(u!=);
  127. stk[++top] = u;
  128. }
  129. }
  130. bool cmp( int u, int v ) {
  131. return dfn[u]<dfn[v];
  132. }
  133. void effort( char *s ) {
  134. fetch(s);
  135. sort( stk+, stk++top, cmp );
  136. eff[stk[]] += ;
  137. for( int i=; i<=top; i++ ) {
  138. int u=stk[i];
  139. int ca=lca(u,stk[i-]);
  140. eff[u] += ;
  141. eff[ca] -= ;
  142. }
  143. }
  144. void bfs() {
  145. qu[bg=ed=] = ;
  146. while( bg<=ed ) {
  147. int u=qu[bg++];
  148. for( int t=head[u]; t; t=next[t] ) {
  149. int v=dest[t];
  150. qu[++ed] = v;
  151. }
  152. }
  153. for( int i=ed; i>=; i-- ) {
  154. int u=qu[i];
  155. for( int t=head[u]; t; t=next[t] ) {
  156. int v=dest[t];
  157. eff[u] += eff[v];
  158. }
  159. }
  160. dp[] = ;
  161. for( int i=; i<=ed; i++ ) {
  162. int u=qu[i];
  163. if( eff[u]>=k ) {
  164. dp[u] = dp[pnt[u]]+dp[u];
  165. } else {
  166. dp[u] = dp[pnt[u]];
  167. }
  168. }
  169. }
  170. void query( char *s ) {
  171. fetch(s);
  172. sort( stk+, stk++top, cmp );
  173. dnt rt = ;
  174. for( int i=; i<=top; i++ ) {
  175. int u=stk[i];
  176. rt += dp[u];
  177. }
  178. printf( "%lld ", rt );
  179. }
  180. int main() {
  181. // input and build sam
  182. scanf( "%d%d", &n, &k );
  183. init();
  184. char *buf_cur = buf;
  185. for( int i=; i<=n; i++ ) {
  186. shead[i] = buf_cur;
  187. scanf( "%s", shead[i] );
  188. for( int j=; shead[i][j]; j++ )
  189. append( shead[i][j]-'a'+ );
  190. append( );
  191. buf_cur += strlen(shead[i]) + ;
  192. }
  193. log[] = -;
  194. for( int i=; i<=ntot; i++ ) log[i] = log[i>>]+;
  195. // dodp to calc the number of real substring
  196. dodp();
  197. // build parent tree and its dfs order
  198. build();
  199. dfs();
  200. // calc echo string's effort
  201. for( int i=; i<=n; i++ )
  202. effort( shead[i] );
  203. // bfs to calc the sum of subans
  204. bfs();
  205. // query the answer of eacho string
  206. for( int i=; i<=n; i++ )
  207. query( shead[i] );
  208. printf( "\n" );
  209. }

bzoj 3473 后缀自动机多字符串的子串处理方法的更多相关文章

  1. POJ 1509 Glass Beads 后缀自动机 模板 字符串的最小表示

    http://poj.org/problem?id=1509 后缀自动机其实就是一个压缩储存空间时间(对节点重复利用)的储存所有一个字符串所有子串的trie树,如果想不起来长什么样子可以百度一下找个图 ...

  2. Lexicographical Substring Search (spoj7259) (sam(后缀自动机)+第k小子串)

    Little Daniel loves to play with strings! He always finds different ways to have fun with strings! K ...

  3. SPOJ LCS 后缀自动机找最大公共子串

    这里用第一个字符串构建完成后缀自动机以后 不断用第二个字符串从左往右沿着后缀自动机往前走,如能找到,那么当前匹配配数加1 如果找不到,那么就不断沿着后缀树不断往前找到所能匹配到当前字符的最大长度,然后 ...

  4. [SPOJ1811]Longest Common Substring 后缀自动机 最长公共子串

    题目链接:http://www.spoj.com/problems/LCS/ 题意如题目,求两个串的最大公共子串LCS. 首先对其中一个字符串A建立SAM,然后用另一个字符串B在上面跑. 用一个变量L ...

  5. SPOJ - SUBST1 New Distinct Substrings —— 后缀数组 单个字符串的子串个数

    题目链接:https://vjudge.net/problem/SPOJ-SUBST1 SUBST1 - New Distinct Substrings #suffix-array-8 Given a ...

  6. bzoj 3676 后缀自动机+马拉车+树上倍增

    思路:用马拉车把一个串中的回文串个数降到O(n)级别,然后每个串在后缀自动机上倍增找个数. #include<bits/stdc++.h> #define LL long long #de ...

  7. 不在B中的A的子串数量 HDU - 4416 (后缀自动机模板题目)

    题目: 给定一个字符串a,又给定一系列b字符串,求字符串a的子串不在b中出现的个数. 题解: 先将所有的查询串放入后缀自动机(每次将sam.last=1)(算出所有子串个数) 然后将母串放入后缀自动机 ...

  8. BZOJ 4327 JSOI2012 玄武密码(后缀自动机)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4327 [题目大意] 求每个子串在母串中的最长匹配 [题解] 对母串建立后缀自动机,用每 ...

  9. 【算法专题】后缀自动机SAM

    后缀自动机是用于识别子串的自动机. 学习推荐:陈立杰讲稿,本文记录重点部分和感性理解(论文语言比较严格). 刷题推荐:[后缀自动机初探],题目都来自BZOJ. [Right集合] 后缀自动机真正优于后 ...

随机推荐

  1. 洛谷 P4559: bzoj 5319: [JSOI2018]军训列队

    题目传送门:洛谷 P4559. 题意简述: 有 \(n\) 个学生,编号为 \(i\) 的学生有一个位置 \(a_i\). 有 \(m\) 个询问,每次询问编号在 \([l,r]\) 区间内的学生跑到 ...

  2. 【FCS NOI2018】福建省冬摸鱼笔记 day3

    第三天. 计算几何,讲师:叶芃(péng). dalao们日常不记笔记.@ghostfly233说他都知道了,就盼着自适应辛普森积分. 我计算几何基础不好……然而还是没怎么讲实现,感觉没听什么东西进去 ...

  3. 手动实现图片预览-放大缩小全屏支持IE9以上

    #{extends '/Index/index.html' /} #{set title:'意见反馈' /} <script src="/public/mgr/javascripts/ ...

  4. js实现table导出Excel,保留table样式

    浏览器环境:谷歌浏览器 1.在导出Excel的时候,保存table的样式,有2种方法,①是在table的行内写style样式,②是在模板里面添加样式 2.第一种方式:行内添加样式 <td sty ...

  5. linux快速安装mysql教程

    #安装mysql服务器:yum install mysql-server #设置开机启动chkconfig mysqld on#现在启动服务service mysqld start #设置root初始 ...

  6. linux之发送邮件--sendmail服务配置

    新手入门也不知道什么日志分析服务好,鸟哥说logwatch,那我就从logwatch开始吧! logwatch用到了emai发邮件,先从配置邮件发送sendmail开始: 安装sendmail服务,我 ...

  7. 签名DLL

    签名DLL 首先需要一个密钥文件,后缀为.snk 密钥文件使用sn.exe 创建: sn.exe /k MySingInKey.snk  sn.exe 工具的具体使用,可以通过 sn.exe /h 或 ...

  8. HDU 1669 Jamie's Contact Groups(多重匹配+二分枚举)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1669 题目大意: 给你各个人可以属于的组,把这些人分组,使这些组中人数最多的组人数最少,并输出这个人数 ...

  9. for循环输出菱形

    package com.hanqi; public class lingxing { public static void main(String[] args) { for(int m=1;m< ...

  10. ASP.NET:插件化机制

    概述 nopCommerce的插件机制的核心是使用BuildManager.AddReferencedAssembly将使用Assembly.Load加载的插件程序集添加到应用程序域的引用中.具体实现 ...