后缀trie树
后缀trie树 就是在一个字典树里有且只有一个单词的所有后缀
列出字符串
的所有后缀
MISSISSIPPI
ISSISSIPPI
SSISSIPPI
SISSIPPI
ISSIPPI
SSIPPI
SIPPI
IPPI
PPI
PI
I
后缀trie树就是把所有后缀全部存进字典树里面
接下来是构造后缀trie树
第一种很简单面,就是按照定义将所有后缀依次插入到字典树里面
第二种就是一个很神奇的操作
比如说
abaab
baab
aab
ab
b
先把所有后缀右对齐,然后从上到下开始操作
先开始把第一个字符插进去
红色箭头的指向的节点和子叶节点都是下一个字符要插入的点
然后在该节点下插入该字符,并且让红色箭头指向刚才插入的那个节点
要是红色箭头下面有要插入的字符,那就不必新建节点,直接指向该节点
后缀自动机 (SAM)
后缀自动机是一张有向无环图,其中顶点是状态,而边代表了状态之间的转移。
初始状态被记作
,终止状态用星号
标记
失配指针fail指向的节点 是 当前节点 (top) 所表示的字符串的最大后缀
从起点开始构建,第一个字符直接插入在根节点后面,失配指针指向根节点
因为第二个节有最大后缀 a 所以不需要在建一个新的节点
要是没有最大后缀,那就只能从根节点出发,连接该节点
你会发现从根节点出发到标
节点的所有路径表示的就是这个字符串的所有后缀
现在就是用到fail的时候了
要是第二次因为同一条边而fail到同一个节点,那么这个节点就得往下新建一个节点
新建的这个节点就有了第一次访问这个节点的性质,
并且让当前节点指向和第一次节点的fail指向新建的节点
完善这个新建的节点
fail指针主要还是用来完善新建节点的,并且加速图的建立
构造后缀自动机思路:
从当前字符(end)的前一个(last)开始跑fail,要是fail指向的节点不存在这个字符,那么将两个节点相连
跑到结束的时候会有两种情况
第一种是跑的了根节点(0)所指的fail,那么直接将根节点和最后节点(end)相连
第二种就是fail指向的节点下有这个字符,那么需要判断这个节点所表示的字符串是不是与(end)所表示的字符串相同
要是不相同,就要新建一个节点(np),新建的这个节点将保存先前访问过这个节点的信息
然后继续跑fail 要是fail指向的节点存在这个这个字符,那么连接节点(np)
目的就是找最长后缀相等的前缀
code
/*
Glass Beads
POJ - 1509
https://cn.vjudge.net/problem/POJ-1509#author=hzoi2017_gyz
题意:给一个字符串S,每次可以将它的第一个字符移到最后面,求这样能得到的字典序最小的字符串。 如BBAAB,最小的就是AABBB
解法:后缀自动机
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#define maxn 100000
using namespace std;
struct node{
int len,fail;
int next[26];
void init(){
fail=-1;
len=0;
memset(next,-1,sizeof next);
}
}a[maxn];
int top,last;
char s[maxn];
void init(){
top=last=0;
a[top++].init();
}
int newnode(){
a[top].init();
return top++;
}
void add(int c){
int end=newnode();
int now=last;
a[end].len=a[last].len+1;
for(;now!=-1&&a[now].next[c]==-1; now=a[now].fail){
a[now].next[c]=end;
}
if(now==-1) a[end].fail=0;
else{
int next=a[now].next[c];
if(a[now].len+1==a[next].len) a[end].fail=next;
else{
int np = newnode();
a[np]=a[next];
a[np].len=a[now].len+1;
a[end].fail=a[next].fail = np;
for(;now!=-1&&a[now].next[c]==next;now=a[now].fail){
a[now].next[c] = np;
}
}
}
last = end;
}
int main(){
int T;
cin>>T;
while(T--){
init();
cin>>s;
int len=strlen(s);
for(int i=0;i<len*2;i++){
add(s[i%len]-'a');
}
int ans=0;
for(int i=0;i<len;i++){
for(int j=0;j<26;j++){
if(a[ans].next[j]!=-1){
ans=a[ans].next[j];
break;
}
}
}
cout<<a[ans].len-len+1<<endl;
}
}
更多例题在这里