hdu 5008 Boring String Problem(后缀自动机构造后缀树)
题意:给出一个字符串s,然后每次询问一个k,求s的所有子串中,字典序第k小的是谁?多个解,则输出最左边的那个
解题思路:这道题应该是为后缀树量身定制的吧。只要构造出了后缀树,然后按字典序遍历就可以得出每个节点包含的子串的字典序的范围了,而且必然是个连续的区间范围。但是我不会后缀树啊。。比赛的时候突然想到,后缀自动机是可以构造后缀树的,虽然以前没写过,但还是硬着头皮上吧,居然还真的让我给撸出来了。我的做法是这样的,将s翻转后建立sam,因为sam的parent tree就是逆序的后缀树嘛。构建好parent tree后,遍历每个节点,对于节点u,它的字节点集合{v1,v2...vn}之间的关系是,这些节点包含的子串的长为val[u]的后缀都是一样的(当我们翻转s建立sam后,后缀就变前缀了),也就是u的代表串,而它们的长为val[u]+1的后缀必然又都是不一样的。我们把{v1,v2..vn}按不一样的那个字符的字典序排序,就得到了后缀树了。那么每个节点的代表串最先出现的位置怎么确定的呢?在逆序构造的后缀自动机中,最现出现的前缀,即为最后出现的后缀。我们要在后缀自动机中确定最后出现的位置,只要将每个前缀出现的位置更新到相应的节点,然后往上更新即可。
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define ll __int64
using namespace std ;
const int maxn = 211111 ;
vector<int> vec[maxn<<1] ;
char s[maxn] ;
int len ;
int fa[maxn<<1] , val[maxn<<1] , c[26][maxn<<1] ;
int tot , last , p[maxn<<1] ;
int wv[maxn] , ws[maxn] , pos[maxn<<1] ;
ll T , gg[maxn<<1] ;
inline int new_node ( int _val ) {
val[++tot] = _val ;
for ( int i = 0 ; i < 26 ; i ++ ) c[i][tot] = 0 ;
fa[tot] = p[tot] = 0 ;
vec[tot].clear () ;
return tot ;
}
void add ( int k ) {
int p = last , i ;
int np = new_node ( val[p] + 1 ) ;
while ( p && !c[k][p] ) c[k][p] = np , p = fa[p] ;
if ( !p ) fa[np] = 1 ;
else {
int q = c[k][p] ;
if ( val[q] == val[p] + 1 ) fa[np] = q ;
else {
int nq = new_node ( val[p] + 1 ) ;
for ( i = 0 ; i < 26 ; i ++ )
c[i][nq] = c[i][q] ;
fa[nq] = fa[q] ;
fa[q] = fa[np] = nq ;
while ( p && c[k][p] == q ) c[k][p] = nq , p = fa[p] ;
}
}
last = np ;
}
void init () {
T = tot = 0 ;
last = new_node ( 0 ) ;
}
void SORT () {
for ( int i = 0 ; i < maxn ; i ++ ) wv[i] = 0 ;
for ( int i = 1 ; i <= tot ; i ++ ) wv[val[i]] ++ ;
for ( int i = 1 ; i < maxn ; i ++ ) wv[i] += wv[i-1] ;
for ( int i = 1 ; i <= tot ; i ++ ) ws[wv[val[i]]--] = i ;
}
int cmp ( int i , int j ) {
int w = fa[i] ;
int l = val[w] ;
i = s[p[i]-l] ;
j = s[p[j]-l] ;
return i < j ;
}
void dfs ( int u ) {
T += val[u] - val[fa[u]] ;
gg[u] = T ;
int k = vec[u].size () ;
for ( int i = 0 ; i < k ; i ++ )
dfs ( vec[u][i] ) ;
}
int cmp1 ( int i , int j ) {
return gg[i] < gg[j] ;
}
void solve () {
SORT () ;
for ( int i = 2 ; i <= tot ; i ++ )
vec[fa[i]].push_back ( i ) ;
int now = 1 ;
for ( int i = 0 ; i < len ; i ++ ) {
int k = s[i] - 'a' ;
now = c[k][now] ;
p[now] = i ;
}
for ( int i = tot ; i >= 1 ; i -- ) {
int v = ws[i] ;
p[fa[v]] = max ( p[v] , p[fa[v]] ) ;
pos[i] = i ;
}
for ( int i = 1 ; i <= tot ; i ++ )
sort ( vec[i].begin() , vec[i].end () , cmp ) ;
dfs ( 1 ) ;
/* for ( int i = 1 ; i <= tot ; i ++ )
printf ( "%d " , gg[i] ) ;
puts( "" ) ;*/
sort ( pos + 1 , pos + tot + 1 , cmp1 ) ;
}
void query ( ll& l , ll& r , ll v ) {
ll k = ( l ^ r ^ v ) + 1 ;
if ( k > T ) {
l = 0 , r = 0 ;
} else {
int L = 1 , R = tot ;
while ( L <= R ) {
int m = ( L + R ) >> 1 ;
if ( gg[pos[m]] >= k ) R = m - 1 ;
else L = m + 1 ;
}
R = L ;
int ans = pos[R] ;
k -= gg[pos[R-1]] ;
l = len - p[ans] ;
r = l + val[fa[ans]] + k - 1 ;
}
printf ( "%I64d %I64d\n" , l , r ) ;
}
int main () {
while ( scanf ( "%s" , s ) != EOF ) {
ll l = 0 , r = 0 ;
len = strlen ( s ) ;
reverse ( s , s + len ) ;
init () ;
for ( int i = 0 ; i < len ; i ++ )
add ( s[i] - 'a' ) ;
solve () ;
int q ;
scanf ( "%d" , &q ) ;
while ( q -- ) {
ll v ;
scanf ( "%I64d" , &v ) ;
query ( l , r , v ) ;
}
}
}