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

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

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

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

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

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

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

 /**************************************************************
Problem: 3473
User: idy002
Language: C++
Result: Accepted
Time:728 ms
Memory:120928 kb
****************************************************************/ #include <cstdio>
#include <cstring>
#include <cassert>
#include <algorithm>
#define N 200010
#define S 500010
#define P 18
using namespace std; typedef long long dnt; int n, k;
char buf[N], *shead[N];
int son[S][], pnt[S], val[S], ntot, last;
int head[S], dest[S], next[S], etot;
int dfn[S], dep[S], anc[S][P+], idgr[S], stk[S], qu[S], top, bg, ed, idc;
dnt dp[S], eff[S];
int log[S]; void init() {
ntot = last = ;
pnt[] = -;
}
void append( int c ) {
int p = last;
int np = ++ntot;
val[np] = val[p]+;
while( p!=- && !son[p][c] )
son[p][c]=np, p=pnt[p];
if( p==- ) {
pnt[np] = ;
} else {
int q=son[p][c];
if( val[q]==val[p]+ ) {
pnt[np] = q;
} else {
int nq = ++ntot;
memcpy( son[nq], son[q], sizeof(son[nq]) );
val[nq] = val[p]+;
pnt[nq] = pnt[q];
pnt[q] = pnt[np] = nq;
while( p!=- && son[p][c]==q )
son[p][c]=nq, p=pnt[p];
}
}
last = np;
}
void make_topo() {
for( int u=; u<=ntot; u++ ) {
for( int c=; c<=; c++ ) {
int v=son[u][c];
if( !v ) continue;
idgr[v]++;
}
}
qu[bg=ed=] = ;
while( bg<=ed ) {
int u=qu[bg++];
for( int c=; c<=; c++ ) {
int v=son[u][c];
if( !v ) continue;
idgr[v]--;
if( idgr[v]== )
qu[++ed] = v;
}
}
}
void dodp() {
make_topo();
dp[] = ;
for( int i=; i<=ed; i++ ) {
int u=qu[i];
for( int c=; c<=; c++ ) {
int v=son[u][c];
if( !v ) continue;
dp[v] += dp[u];
}
}
}
void adde( int u, int v ) {
etot++;
dest[etot] = v;
next[etot] = head[u];
head[u] = etot;
}
void build() {
for( int u=; u<=ntot; u++ )
adde( pnt[u], u );
}
void dfs( int u ) {
dfn[u] = ++idc;
for( int p=; p<=P && anc[u][p-]; p++ )
anc[u][p] = anc[anc[u][p-]][p-];
for( int t=head[u]; t; t=next[t] ) {
int v=dest[t];
dep[v] = dep[u]+;
anc[v][] = u;
dfs(v);
}
}
int lca( int u, int v ) {
if( dep[u]<dep[v] ) swap(u,v);
int t=dep[u]-dep[v];
for( int p=; t; p++,t>>= )
if( t& ) u=anc[u][p];
if( u==v ) return u;
for( int p=log[dep[u]]; anc[u][]!=anc[v][]; p-- )
if( anc[u][p]!=anc[v][p] ) u=anc[u][p],v=anc[v][p];
return anc[u][];
}
void fetch( char *s ) {
top = ;
int u = ;
for( int i=; s[i]; i++ ) {
int c=s[i]-'a'+;
u = son[u][c];
assert(u!=);
stk[++top] = u;
}
}
bool cmp( int u, int v ) {
return dfn[u]<dfn[v];
}
void effort( char *s ) {
fetch(s);
sort( stk+, stk++top, cmp );
eff[stk[]] += ;
for( int i=; i<=top; i++ ) {
int u=stk[i];
int ca=lca(u,stk[i-]);
eff[u] += ;
eff[ca] -= ;
}
}
void bfs() {
qu[bg=ed=] = ;
while( bg<=ed ) {
int u=qu[bg++];
for( int t=head[u]; t; t=next[t] ) {
int v=dest[t];
qu[++ed] = v;
}
}
for( int i=ed; i>=; i-- ) {
int u=qu[i];
for( int t=head[u]; t; t=next[t] ) {
int v=dest[t];
eff[u] += eff[v];
}
}
dp[] = ;
for( int i=; i<=ed; i++ ) {
int u=qu[i];
if( eff[u]>=k ) {
dp[u] = dp[pnt[u]]+dp[u];
} else {
dp[u] = dp[pnt[u]];
}
}
}
void query( char *s ) {
fetch(s);
sort( stk+, stk++top, cmp );
dnt rt = ;
for( int i=; i<=top; i++ ) {
int u=stk[i];
rt += dp[u];
}
printf( "%lld ", rt );
}
int main() {
// input and build sam
scanf( "%d%d", &n, &k );
init();
char *buf_cur = buf;
for( int i=; i<=n; i++ ) {
shead[i] = buf_cur;
scanf( "%s", shead[i] );
for( int j=; shead[i][j]; j++ )
append( shead[i][j]-'a'+ );
append( );
buf_cur += strlen(shead[i]) + ;
}
log[] = -;
for( int i=; i<=ntot; i++ ) log[i] = log[i>>]+;
// dodp to calc the number of real substring
dodp();
// build parent tree and its dfs order
build();
dfs();
// calc echo string's effort
for( int i=; i<=n; i++ )
effort( shead[i] );
// bfs to calc the sum of subans
bfs();
// query the answer of eacho string
for( int i=; i<=n; i++ )
query( shead[i] );
printf( "\n" );
}

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. 使用Netcat进行攻击

    https://www.freebuf.com/column/135007.html 在网上找到了一个开启了ftp服务的服务: http://static.vhdong.com/Upload/Temp ...

  2. 移动端测试=== adb 无线连接手机

    无线连接(需要借助 USB 线) 除了可以通过 USB 连接设备与电脑来使用 adb,也可以通过无线连接——虽然连接过程中也有需要使用 USB 的步骤,但是连接成功之后你的设备就可以在一定范围内摆脱 ...

  3. 被我误解的max_connect_errors【转】

    实为吾之愚见,望诸君酌之!闻过则喜,与君共勉 第一节  什么是max_connect_errors 一开始接触这个参数的时候,感觉他和max_connections的含义差不多,字面意思简单明了,这个 ...

  4. Linux的bg和fg命令 ---让程序在前台后台之间切换

    Linux的bg和fg命令 我们都知道,在 Windows 上面,我们要么让一个程序作为服务在后台一直运行,要么停止这个服务.而不能让程序在前台后台之间切换.而 Linux 提供了 fg 和 bg 命 ...

  5. Jmeter运行结果unicode编码乱码问题

    一.web页面乱码 比如访问百度返回页面显示乱码,如下会有问号 如果想让他显示中文可以按以下操作: 1.打开jmter配置文件 bin/jmeter.properties 2.修改配置文件,查找“sa ...

  6. Jquery获取radio单选按钮的value与后面的文字

    一组单选按钮如图: <input name="classId" value="8afa94f45ba3e2c1015ba3fac6c00000" type ...

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

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

  8. SVN入门教程总结

    参考: SVN使用笔记 SVN入门必备教程 一看就懂 SVN使用教程总结 版本控制器:SVN教程 菜鸟教程之SVN教程 极客学院之SVN教程 SVN(SubVersion)简介: 为什么要使用SVN( ...

  9. Kubernetes之YAML文件

    一.YAML 基础 YAML是专门用来写配置文件的语言,非常简洁和强大,使用比json更方便.它实质上是一种通用的数据串行化格式.后文会说明定义YAML文件创建Pod和创建Deployment. YA ...

  10. 最大子段和(Max Sum)

    Max Sum. The following is an instance. a)    (-2,11,-4,13,-5,-2) 思路: 最大子段和:给定一个序列(元素可正可负),找出其子序列中元素和 ...