参考链接:http://blog.sina.com.cn/s/blog_7812e98601012cim.html
后缀自动机和后缀数组一样,刚开始学的时候十分不好懂,学完之后觉得自己很厉害了,然后拿到题目就傻眼了
对于字符串这里的很多算法的性质都一样,如果不能完全理解其构造的意义,那么是根本做不了题目的,例如KMP
算法,开始学习的时候next函数是用来匹配用的,后来发现基本上考KMP的题目根本不会涉及到匹配,每次都是
根据next值来判断串具有的性质,所以对next值不理解就无法做题,同样后缀数组,没有题目会叫你输出sa数组
或者rank数组的值的,都会让你根据这两个数组的值,来构造一个其他的什么东西,然后解题,比如说height数组
这里也一样,构造一个后缀自动机也不能说明你对这个算法很熟悉,只能说明,你知道有这么个算法!
表示目前对后缀自动机理解有限
后缀自动机的阐述以及构造方法上面的连接已经说明的很清楚了,感谢那位博主的分享
下面我阐述一下我学习过程中对遇到问题的理解:
1、为什么当len=len+1的时候直接就处理,不是的时候要copy出来
如果是的话,那么说明这个节点只能有上面一个节点过来,在后缀自动机中一个儿子可以有多个父亲
len表示的从根节点到这个位置的最长路径,如果目前这个父亲个他的儿子相差一个,表示这个儿子
只能由这一个父亲过来,否则就不是,不是的话如果直接处理就会出现问题了
至于为什么要copy出来,这个其实也不太好理解,copy出来,重新设置len值,然后把后面指向原来的
全部、指向这个刚copy出来的节点,这样目的是
1、copy出来,防止直接处理漏掉很多后缀
2、沿路将指向copy前的那个节点的可加后缀的节点全部转移到新的节点,目的是防止出现重复后缀情况
说的很乱,因为还不是很理解
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
#define maxn 10000
char str[maxn];
struct node
{
node *next[26];
node *f;
int len;
node():len(0),f(0)
{
memset(next,0,sizeof(next));
}
}re_root,*tail=&re_root;
int build_suffix_array(char ch)
{
int temp=int(ch-'a');
node *p=new node(),*q=tail,*r;
p->len=tail->len+1;
for( ;q&&!q->next[temp];q=q->f) q->next[temp]=p;
tail=p;
if(q==NULL) tail->f=&re_root;
else
if(q->next[temp]->len==q->len+1) tail->f=q;
else
{
r=new node();
*r=*(q->next[temp]);
r->len=q->len+1;
tail->f=r;
p=q->next[temp];
p->f=r;
for( ;q&&q->next[temp]==p;q=q->f) q->next[temp]=r;
}
return 0;
}
int pos;
int triverse(node *root)
{
bool flag=true;
for(int i=0;i<26;i++)
{
if(root->next[i])
{
str[pos++]='a'+i;
triverse(root->next[i]);
pos--;
flag=false;
}
}
if(flag)
{
str[pos]=0;
printf("%s\n",str);
}
return 0;
}
int main()
{
int i,k,t;
scanf("%s",str);
for(i=0;str[i];i++)
build_suffix_array(str[i]);
printf("Ok\n");
pos=0;
triverse(&re_root);
return 0;
}