广义SAM
参考
还有首先你要会SAM吧~
用途
相比与单串SAM,广义自动机能存储的是多个字符串。
有两种写法,第一种是离线利用trie树结构,第二种是在线伪广义SAM
离线+Trie
首先构建出trie树。
然后在trie树上BFS(),用\(pos[u]\)映射trie树上\(u\)节点对应SAM上的节点。
为什么不dfs,因为时间复杂度是trie树上所有叶子到根的距离和,证明BFS\(O(n)\)复杂度具体见上面参考博客。
code:
struct SAM {
int tr[N<<1][M],nd,len[N<<1],par[N<<1],pos[N*M];
queue<int> Q;
int Insert(int x,int lst) {
...略,返回新节点编号
}
void BFS() {
Q.push(1);pos[1]=1;
while(!Q.empty()) {
int u=Q.front(); Q.pop();
for(int i=0;i<26;i++) {
int v=T.go[u][i];
if(!v)continue;
pos[v]=Insert(i,pos[u]);
Q.push(v);
}
}
}S;
相信上面code的你能够很好的理解
在线
虽然它很伪,但通常跑的比上面那种快,而且对于很多问题在上面处理起来很方便。
方便讲解,先放一份原来插单串的code:
int Insert(int x,int lst) {
int p=lst,np=++nd;len[np]=len[p]+1;
for(;!tr[p][x];p=par[p])tr[p][x]=np;
if(!p) {par[np]=1;}
else {
int q=tr[p][x];
if(len[q]==len[p]+1) {par[np]=q;}
else {
int nq=++nd;par[nq]=par[q];len[nq]=len[p]+1;
for(int j=0;j<26;j++)tr[nq][j]=tr[q][j];
par[q]=par[np]=nq;
for(;tr[p][x]==q;p=par[p]) tr[p][x]=nq;
}
}
return np;
}
多串总体的区别就是每加入一个新串前让lst=1(root)
这样会出现之前插单串没出现过的情况:之前p=lst往上找tr[p][x]!=0之前肯定会存在tr[p][x]=0
然而现在可能一开始tr[lst][x]!=0,这有什么问题吗?就跟原来一样分两类讨论(拆点或不拆点)
问题就在于,这时新加的np是个空点,因为它是完全没有必要的。
令q=tr[lst][x]
- 如果len[q]=len[lst]+1,那此时直接返回q即可。
- 否则len[q]>len[lst]+1,还是要拆出nq,但是在(上面拆点)代码中唯一用到新点(np)的就是par[np]=nq
可len[nq]=len[np]的,nq已经包含了np了……
So Easy!直接不创np这个空点不就行了
我们上面也说明了现在新代码的写法。先判断tr[lst][x]!=0时就不定义新np点……最后return nq,否则写法跟上面单串一样的。 - code
struct SAM {
int tr[N<<1][M],nd,len[N<<1],par[N<<1];
queue<int> Q;
SAM() {nd=1;}
int Insert(int x,int lst) {
int p=lst;
if(tr[p][x]) {
int q=tr[p][x];
if(len[q]==len[p]+1)return q;
int nq=++nd;par[nq]=par[q];len[nq]=len[p]+1;
for(int j=0;j<c;j++)tr[nq][j]=tr[q][j];
for(;tr[p][x]==q;p=par[p]) tr[p][x]=nq;
return par[q]=nq;
}
int np=++nd;len[np]=len[p]+1;
for(;!tr[p][x];p=par[p])tr[p][x]=np;
if(!p) {par[np]=1;}
else {
int q=tr[p][x];
if(len[q]==len[p]+1) {par[np]=q;}
else {
int nq=++nd;par[nq]=par[q];len[nq]=len[p]+1;
for(int j=0;j<c;j++)tr[nq][j]=tr[q][j];
par[q]=par[np]=nq;
for(;tr[p][x]==q;p=par[p]) tr[p][x]=nq;
}
}
return np;
}
}S;
ps.最后强调一点:每个结点中的子串可能来自多个字符串。然而所有来源于相同字符串的子串endpos集合保证相同,而来源于不同字符串的endpos不一定相同.
很好理解(后面加一堆废话),假如加入了i-1个字符串(此时SAM满足上面性质)。第i个字符串的加入,会拆分原来的点(拆点不影响上面性质,只不过是对于同一个字符串同一个等价类被拆成多个节点罢了)。新加的i字符串肯定是满足的。
广义SAM的更多相关文章
- 【HDU 4436】 str2int (广义SAM)
str2int Problem Description In this problem, you are given several strings that contain only digits ...
- 【BZOJ 3926】 [Zjoi2015]诸神眷顾的幻想乡 (广义SAM)
3926: [Zjoi2015]诸神眷顾的幻想乡 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 974 Solved: 573 Descriptio ...
- 【BZOJ 3473】 字符串 (后缀数组+RMQ+二分 | 广义SAM)
3473: 字符串 Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串 ...
- luogu3346 诸神眷顾的幻想乡 (广义SAM)
首先,让每一个叶节点做一次树根的话,每个路径一定至少有一次会变成直上直下的 于是对于每个叶节点作为根产生的20个trie树,把它们建到同一个广义SAM里 建法是对每个trie dfs去建,last就是 ...
- loj#6031. 「雅礼集训 2017 Day1」字符串(SAM 广义SAM 数据分治)
题意 链接 Sol \(10^5\)次询问每次询问\(10^5\)个区间..这种题第一感觉就是根号/数据分治的模型. \(K\)是个定值这个很关键. 考虑\(K\)比较小的情况,可以直接暴力建SAM, ...
- Luogu P3181 [HAOI2016]找相同字符 广义$SAM$
题目链接 \(Click\) \(Here\) 设一个串\(s\)在\(A\)中出现\(cnt[s][1]\)次,在\(B\)中出现\(cnt[s][2]\)次,我们要求的就是: \[\sum cnt ...
- CF666E Forensic Examination 广义SAM、线段树合并、倍增、扫描线
传送门 朴素想法:对\(M\)个匹配串\(T_1,...,T_M\)建立广义SAM,对于每一次询问,找到这个SAM上\(S[pl...pr]\)对应的状态,然后计算出对于每一个\(i \in [l,r ...
- Luogu4022 CTSC2012 熟悉的文章 广义SAM、二分答案、单调队列
传送门 先将所有模板串扔进广义SAM.发现作文的\(L0\)具有单调性,即\(L0\)更小不会影响答案,所以二分答案. 假设当前二分的值为\(mid\),将当前的作文放到广义SAM上匹配. 设对于第\ ...
- BZOJ3926 ZJOI2015 诸神眷顾的幻想乡 Trie、广义SAM
传送门 树上的任意一条路径一定会在以某一个叶子节点为根的树上成为一条直上直下的链,而总共只有\(20\)个叶子节点. 于是每一次选所有叶子节点中的一个作为根,形成一个\(Trie\),把\(20\)个 ...
- hdu6405Make ZYB Happy 广义sam
题意:给出n(n<=10000)个字符串S[1~n],每个S[i]有权值val[i],随机等概率造一个由小写字母构成的字符串T,Sum = 所有含有子串T的S[i]的val[i]之积,求Sum的 ...
随机推荐
- oracle 多列求和
第一种: select sum(decode(count1,null,0,count1) +decode(count2,null,0,count2) +decode(count3,null,0,cou ...
- 谈谈Spring中都用到了哪些设计模式?
谈谈Spring中都用到了哪些设计模式? JDK 中用到了那些设计模式?Spring 中用到了那些设计模式?这两个问题,在面试中比较常见.我在网上搜索了一下关于 Spring 中设计模式的讲解几乎都是 ...
- 最长非降子序列的N*logN解法
之前讲到过求最长非降子序列的O(N^2)解法. 链接 这次在原来的基础上介绍一下N*logN解法. 该解法主要是维护一个数组minValue,minValue[i]表示最长上身子序列长度为i的数的最小 ...
- Mybatis插入数据
对上文->Mybatis快速入门-<进行代码修改 1.在UserMapper.xml中添加插入操作 <!-- 插入操作--> <insert id="save& ...
- JavaWeb学习day2-web入门&随笔
Tomcat详解: 1默认端口号: Tomcat:8080 Mysql:3306 http:80 https:443 2默认主机名:localhost 地址:127.0.0.1 3网站应用默认存放位置 ...
- 『现学现忘』Git基础 — 13、Git的基础操作
目录 1.Git最基础的使用方式 (1)初始化本地版本库 (2)查看文件的状态 (3)把文件添加到暂存区 (4)把暂存区的内容提交到本地版本库 2.总结本文用到的Git命令 1.Git最基础的使用方式 ...
- 漏洞复现:MS12-020 远程桌面协议RDP远程代码执行漏洞
漏洞复现:MS12-020 远程桌面协议RDP远程代码执行漏洞 攻击机:Kali2019 靶机:Win7 64位 解题步骤: 1.打开Kali2019和Win7 64位 ,确定IP地址是多少 2.确定 ...
- 红旗 Linux 桌面操作系统11来了:支持国产自主CPU,全新UI风格设计,兼容面广...
链接:https://reurl.cc/g8ke9X 红旗Linux桌面操作系统11将于1月10日开放预览版的下载,新版本具有良好的硬件兼容,支持多款国产自主CPU品牌,同时还具有丰富的外设支持及海量 ...
- 老生常谈系列之Aop--AspectJ
老生常谈系列之Aop--AspectJ 这篇文章的目的是大概讲解AspectJ是什么,所以这个文章会花比较长的篇幅去解释一些概念(这对于日常开发来说没一点卵用,但我就是想写),本文主要参考Aspect ...
- AspNetCore7.0源码解读之UseMiddleware
UseMiddlewareExtensions 前言 本文编写时源码参考github仓库主分支. aspnetcore提供了Use方法供开发者自定义中间件,该方法接收一个委托对象,该委托接收一个R ...