同步个人博客 http://sxysxy.org/blogs/23 到csdn)
给一字符串的所有后缀排个序输出,字符串长度 <= 100000
排序要求字典序小的在前面,在满足这个条件的情况下,短的后缀放到前面
例如ababa的后缀排序结果是[a, aba, ababa, ba, baba]。
暴力做法很容易想到,枚举出字符串s的总共length(s)个后缀,sort一下,然而time limit exceed (
把后缀都插入字典树? time limit exceed & memory limit exceed。
所以这个时候就需要后缀自动机登场了。
后缀排序可以看作是给字符串所有子串排序的一个特殊情况,就是子串的右端点是字符串的末尾。
对与子串的排序,我们可以构造完后缀自动机然后dfs,每次先走字典序小的字符,最后遍历完毕就是子串排序的结果。
只排序后缀呢?
今天上午英语课上我手画了一个字符串的后缀自动机然后脑补出来了一个这样的方法:
我们从后缀自动机最后一个状态St出发,向上走后缀链接树(也有叫”parent树”的),途经的状态节点可以接受的最长的字符串都是St可接受的最长的字符串的后缀。而St可接受的最长的字符串恰好就是整个字符串str(因为St是最后一个状态)。也就是说,从St出发走后缀链接一直到顶,恰好可以途径字符串str的所有后缀。
那么后缀排序就可以把从St出发走后缀链接到顶途径的所有节点都标记一下,之后像子串排序一样dfs这个后缀自动机,只在打了标记的点那里输出,就可以得到后缀排序的结果了。最后总的时间复杂度还是O(n),SAM很强啊!!!
代码
#include <cstdio>
#include <cstring>
#include <cstdarg>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 1e6+2;
const int SIGMA = 26;
const int BASE = (int)'a';
class SAM
{
public:
int last, size;
struct state
{
int link, len;
int next[SIGMA];
bool mark;
void init()
{
link = -1;
len = 0;
mark = false;
memset(next, 0, sizeof(next));
}
}st[MAXN<<1];
int newst()
{
st[size++].init();
return size-1;
}
SAM()
{
last = size = 0;
newst();
}
void expand(char newc)
{
int c = newc-BASE;
int cur = newst();
st[cur].len = st[last].len+1;
int p;
for(p = last; p != -1 && !st[p].next[c]; p = st[p].link)
st[p].next[c] = cur;
if(p == -1)
st[cur].link = 0;
else
{
int q = st[p].next[c];
if(st[q].len == st[p].len + 1)
st[cur].link = q;
else
{
int clone = newst();
st[clone].len = st[p].len + 1;
st[clone].link = st[q].link;
memcpy(st[clone].next, st[q].next, sizeof(st[q].next));
for(; p != -1 && st[p].next[c] == q; p = st[p].link)
st[p].next[c] = clone;
st[q].link = st[cur].link = clone;
}
}
last = cur;
}
char buf[MAXN];
int top;
void dfs(int u)
{
if(top && st[u].mark)puts(buf);
state &cur = st[u];
for(int i = 0; i < SIGMA; i++)
{
if(cur.next[i])
{
buf[top++] = i+BASE;
buf[top] = 0;
dfs(cur.next[i]);
top--;
}
}
}
void suffix_sort()
{
top = 0;
//从最后一个状态开始,走后缀链接到顶,途径节点打上标记。
for(int p = last; p; p = st[p].link)
st[p].mark = true;
dfs(0);
}
}sam;
int main()
{
char c;
while(c = getchar())
if(c >= 'a' && c <= 'z')break;
sam.expand(c);
while(c = getchar())
if(c >= 'a' && c <= 'z')sam.expand(c);
else break;
sam.suffix_sort();
return 0;
}