广义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的 ...
随机推荐
- Codepen 每日精选(2018-4-6)
按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以打开原始页面. 按照画出的路线吃豆子的交互动画https://codepen.io/createjs/f... 模拟真实手 ...
- 体温填报app作业演示
今日学习 今天把这个体温填写app作业,做完了. 具体开发流程:https://www.cnblogs.com/yuxuan-light-of-Taihu-Lake/p/14362107.html 点 ...
- jsp笔记---标签
<meta>标签 <meta> 标签提供了 HTML 文档的元数据.元数据不会显示在客户端,但是会被浏览器解析. META元素通常用于指定网页的描述,关键词,文件的最后修改时间 ...
- CSS简单样式练习(二)
运行效果: 源代码: 1 <!DOCTYPE html> 2 <html lang="zh"> 3 <head> 4 <meta char ...
- nodejs全局对象简析
Global:全局变量 定时器.控制台输出.事件 模块化相关的一些全局变量 path/url相关的一些全局变量 编码相关的 buffer:缓存(简单介绍) Process:进程(重点解析) 一.Glo ...
- 小程序安卓端播放不了音频解决方法wx.createInnerAudioContext()
在小程序播放音频时,使用组件wx.createInnerAudioContext(),安卓端无法播放音频. 我的情况:播放服务器上传来的音频,格式为mp3.首先查看你的格式是否符合文档要求 在安卓端进 ...
- ABP源码分析 - 约定注册(3)
入口 //ConfigureServices foreach (var module in Modules) { if (module.Instance is AbpModule abpModule) ...
- SpringMVC基于注解开发的步骤
基于xml配置 .1准备好以下相关jar包 .2创建Maven项目使用骨架 (这里选择第二个以webapp结尾的非第一个) 给项目起个名字 这里可以更改maven本地仓库(依赖包所存放的地方)的路径 ...
- oracle各种用户登录的方式
Oracle有3种用户: system.sys.scott 1.system和sys的差别在与是否能创建数据库2.sys用户登录创建数据库,3.scott是给刚開始学习的人学习的用户.学习者能够用Sc ...
- Ubu18下cpptest安装
1.环境安装 sudo apt install g++ sudo apt install automake sudo apt install autoconf sudo apt install lib ...