后缀自动机

循环移位(后缀自动机)

谁都会走 提交于 2019-11-28 01:26:45
题目描述: 题解: #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=300010; char s[N]; int n,lst=1,tot=1; int len[N*2],ch[N*2][26],fa[N*2],pos[N*2]; //pos表示在原串中的位置 int sum[N][26]; ll ans; void add(int c){ int p=lst,np=lst=++tot; pos[np]=len[np]=len[p]+1; for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np; if(!p) fa[np]=1; else{ int q=ch[p][c]; if(len[q]==len[p]+1) fa[np]=q; else{ int nq=++tot; memcpy(ch[nq],ch[q],sizeof(ch[q])); fa[nq]=fa[q]; len[nq]=len[p]+1; pos[nq]=pos[q]; fa[q]=fa[np]=nq; for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq; } } return; } int main(){ scanf("%s",s+1); n=strlen

[数据结构]后缀自动机

偶尔善良 提交于 2019-11-27 12:33:44
前言 对于字符串 \(s\) , \(|s|\) 表示s的长度 对于字符集 \(A\) , \(|A|\) 表示 \(A\) 的大小 本文字符串下标一律从0开始 。 本文字数较多,如有错别字或者概念性错误,请联系博主或在下方回复。 SAM 后缀自动机 (suffix automaton, SAM) 是一种解决多种字符串问题的数据结构。 SAM基于一个字符串构建的,是给定字符串的所有子串的压缩形式。 标准定义为: 字符串 \(s\) 的SAM是一个接受 \(s\) 的所有后缀的最小 \(\texttt{DFA}\) (确定性有限自动机或确定性有限状态自动机) 构造SAM 我们记 \(t_0\) 为字符串的一个虚拟源点,事实上这种操作(构造虚拟节点)应用非常广泛。 那么SAM应当是: 有向无环图,节点为状态,边叫做转移 所有节点都可以由 \(t_0\) 到达 每个转移代表一个字母,且任意一个状态的出边的字母不同 存在一个或多个 终止状态 ,使得从 $t_0 $ 到终止状态上的所有转移依访问顺序排列,对应原字符串 \(s\) 的某个后缀,且 \(s\) 的任何后缀均可以上述方式描述。 SAM是满足以上的条件节点数最小的自动机 简单来说,没有后缀链接的SAM是一棵以 \(t_0\) 为源点的无向图。这个图的名字叫做后缀链接树。 没有后缀链接的SAM的样子 目前您暂且不需要知道后缀链接是什么

P3804 【模板】后缀自动机

我的未来我决定 提交于 2019-11-26 22:18:06
题面: https://www.luogu.org/problem/P3804 由于i在link[i]处断裂,所以两者的maxlen已经不同,所以我们要将断裂的点重新接上,也就是从link[i]向i建边,最后跑一边dfs统计答案即可。 Code: #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<ctime> using namespace std; const int N=2000010,M=26; int Size=1,Last=1,trans[N][M],maxlen[N],link[N],head[N],cnt,len; long long sz[N],ans; char s[N]; struct Edge{ int to,Next; }edge[N]; void add(int u,int v){ ++cnt; edge[cnt].to=v; edge[cnt].Next=head[u]; head[u]=cnt; } void extend(int id){ int cur=++Size,p; sz[Size]=1; maxlen[cur]=maxlen[Last]+1; for (p

后缀自动机

纵饮孤独 提交于 2019-11-26 19:32:34
题目链接:http://icpc.upc.edu.cn/problem.php?cid=1828&pid=7 #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 5; char str[maxn]; int hou[maxn *2][30],link[maxn *2], num[maxn *2]; ll End[maxn *2]; int stra[maxn], strb[maxn *2],Size, last, len; void add(int c){ int p = last,np = ++Size; last = np,End[np] = 1; num[np] = num[p] + 1; while(!hou[p][c] && p) hou[p][c] = np,p = link[p]; if(p == 0) link[np] = 1; else{ int q = hou[p][c]; if(num[p] + 1 == num[q]) link[np] = q; else{ int temp = ++Size; memcpy(hou[temp], hou[q], sizeof(hou[q])); num[temp] = num[p] + 1;

后缀自动机

爱⌒轻易说出口 提交于 2019-11-26 17:00:02
后缀自动机是一个很复杂的算法,下面讲一讲建图和应用。 建图 首先要明确后缀自动机上点和含义:一个点代表的是一类有相同后缀的字符串,而每一个点的fail一定是它本身的后缀。 那么我们定义如下结构体: struct point { int len; int fa; int ch[30]; }; point a[MAXN]; 其中fa就是fail,ch表示点的出边(a~z),len就表示这个点能表示的最大后缀长度。 然后对于一个字符串,我们每次加入一个点。 设当前点为np,之前的主干上的最后一个点为p,当前加入的字符为c。 显然,a[np].len=a[p].len+1(因为从a[p].len出来加上一个c字符就到了np)。 接着我们就要求fail,这一个要分类讨论。 我们从np开始跳fail链,要保证跳到的点的没有c出边,对于这些点,我们把它的c出边设为np。 然后如果直接跳到了1都还没有发现一个有c出边的点,那么就把a[np].fail设为1(这个是case1)。 如果找到了一个有c出边的点,设它的出边连向q,那么就判断一下a[q].len==a[p].len+1(注意此时p是那个有c出边的点)。 如果相等,那么a[np].fa=q,这一个是没问题的了(case2)。 否则我们还要新建一个nq,使a[nq].len=a[p].len+1,然后把q的出边信息全部赋给nq。接下来,a

后缀自动机求多串LCS——spojlcs2

﹥>﹥吖頭↗ 提交于 2019-11-26 11:10:32
/* 每个状态存最长匹配长度,然后多个串匹配过程中取最小的最长匹配长度 和LCS1不同的地方:LCS只要维护住当前匹配长度和最长匹配长度即可,但是多串匹配需要维护的是每个状态结点(即后缀树上)的信息 所以对每个状态存下两个值Max,Min,分别表示该状态对于该串的最长匹配长度,以及所有已经匹配过的串在该状态下的最小的最长匹配长度 在对一个串进行匹配后,在后缀树上自底向上回溯一次,更新Max值 更新完Max后再更新Min */ #include<bits/stdc++.h> using namespace std; #define maxn 400005 struct SAM{ int last,cnt; int nxt[maxn][26],len[maxn],link[maxn]; SAM(){last=cnt=1;} void add(int c){ int p=last,np=last=++cnt; len[np]=len[p]+1; for(;p&&!nxt[p][c];p=link[p]) nxt[p][c]=np; if(!p){link[np]=1;return;} int q=nxt[p][c]; if(len[q]==len[p]+1){link[np]=q;return;} int clone=++cnt; link[clone]=link[q]; len

后缀自动机

泪湿孤枕 提交于 2019-11-26 10:29:48
https://oi-wiki.org/string/sam/#_5 oiwiki网上的 https://blog.csdn.net/thy_asdf/article/details/51569443 这个博客讲了很多题。 //#include<bits/stdc++.h> #include<cstdio> #include<cstring> #include<queue> #include<vector> #include<algorithm> using namespace std; const int maxn = 2e4 + 5; const int maxc = 180;//如果太大可以用map const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; typedef long long ll; int len[maxn * 2]; //最长子串的长度(该节点字串数量=len[x]-len[fail[x]]) int fail[maxn * 2]; //后缀链接(最短串前部减少一个字符所到达的状态) int cnt[maxn * 2]; //被后缀连接的数量,方法一求拓扑排序时需要。 int nex[maxn * 2][maxc]; //状态转移(尾部加一个字符的下一个状态)(图),如果不只是字母,而是很大的话可以用map.

后缀自动机求LCS——spoj-LCS

妖精的绣舞 提交于 2019-11-25 22:18:36
经典题 注意匹配的时候:用t串去s串的SAM里进行匹配,和字典树一样遍历t中字符,用cur记录当前已经匹配的长度,如果能当前字符能匹配则cur++(这里不能直接用cur=len[now]),反之用link指针进行失配,直到完成匹配后cur=len[now] 为什么匹配成功时不能直接cur=len[now]?因为自动机上的转移是在后面加一个字符,但是不保证前面不加字符,因为每个结点的len是该节点代表的maxlen 但是失配后再转移成功则可以用cur=len[now],因为失配结点代表的最短串长度都有len[now]+1,即到了这个状态,那么t串一定有minlen[now]的长度,所以其link指向的状态的maxlen[now]=minlen[now-1]一定是满足条件的! #include<bits/stdc++.h> using namespace std; #define maxn 250005 struct SAM{ int cnt,last; int nxt[maxn<<1][26]; int link[maxn<<1]; int len[maxn<<1]; SAM(){ cnt=last=1; } void insert(int c){ int p=last,np=last=++cnt; len[np]=len[p]+1; for(;p&&!nxt[p][c];p