hdu 5008 Boring String Problem(后缀自动机构造后缀树)

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 ) ;
        }
    }
}
时间: 2024-12-19 08:45:07

hdu 5008 Boring String Problem(后缀自动机构造后缀树)的相关文章

hdu 5008 Boring String Problem(后缀数组)

题目链接:hdu 5008 Boring String Problem 题目大意:给定一个字符串,初始状态l,r为0,每次询问子串中字典序第l^r^v+1的子串区间,对于重复的输出下标小的. 解题思路:后缀数组,对给定字符串做后缀数组,然后根据height数组确定每个位置做为起点的子串有多少,然后二分查找确定起点位置,但是因为子串的重复的要输出下表小的,所以确定起点后还要确定字典序最小的下标. #include <cstdio> #include <cstring> #includ

HDU 5008 Boring String Problem(西安网络赛B题)

HDU 5008 Boring String Problem 题目链接 思路:构造后缀数组,利用height的数组能预处理出每个字典序开始的前缀和有多少个(其实就是为了去除重复串),然后每次二分查找相应位置,然后在往前往后找一下sa[i]最小的 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int

HDU - 5008 Boring String Problem (后缀数组+二分+RMQ)

Problem Description In this problem, you are given a string s and q queries. For each query, you should answer that when all distinct substrings of string s were sorted lexicographically, which one is the k-th smallest. A substring si...j of the stri

[后缀数组+二分+rmq] hdu 5008 Boring String Problem

有点小可惜这道题,当时整个思路都想到了,就是最后找最左下标的时候不会处理, 然后结束完发现直接暴力就可以了,想到了可是不敢写,10w个a直接就T了啊... 数据太弱了,敢写就过系列啊 T T. 然后希望有大神提供完美思路! 题意: 给一个字符串 然后n次询问 对于每一次询问给一个v 然后问第 l⊕r⊕v+1小的子串的区间   (⊕代表异或) 然后输出l r 这里的l r 就是上一次输出的l r  初始化是0 0 不存在输出0 0  如果多个 输出出现最早的. 思路: 首先后缀数组就不说了,做完之

HDU 5008 Boring String Problem(后缀数组+二分)

题目链接 思路 想到了,但是木写对啊....代码 各种bug,写的乱死了.... 输出最靠前的,比较折腾... #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> #include <map> using namespace std; #define N 501000 #define LL __in

HDU 5008 Boring String Problem

题意:给定一个串长度<=1e5,将其所有的不同的字串按照字典序排序,然后q个询问,每次询问字典序第k小的的起始坐标,并且起始坐标尽量小. 分析: 一开始看错题意,没有意识到是求不同的字串中第k小的,果断不知道怎么做,感觉如果题目改成这样,似乎还有点难度,至少对我来说. 好了,这个题目是考虑不同的字串,首先后缀数组处理,也就是讲后缀按照字典序排序,对于每个后缀开始的字串,如h[i],容易知道i和i-1的后缀的LCP长度为h[i]那么i中除开前h[i]个字串,之后的字串在i-1之前都是没有出现过的,

HDOJ 5008 Boring String Problem

后缀数组+RMQ+二分 后缀数组二分确定第K不同子串的位置 , 二分LCP确定可选的区间范围 , RMQ求范围内最小的sa Boring String Problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 661    Accepted Submission(s): 183 Problem Description In thi

hdu 5008(2014 ACM/ICPC Asia Regional Xi&#39;an Online ) Boring String Problem(后缀数组&amp;二分)

Boring String Problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 219    Accepted Submission(s): 45 Problem Description In this problem, you are given a string s and q queries. For each que

利用后缀数组构造后缀树

由于蒟蒻azui前段时间忙着准备省选,并在省选中闷声滚大粗,博客停更了好久.. 省选过后整个人各种颓,整天玩玩泥巴什么的... 前段时间学后缀数组的时候上网查相关资料,看到说后缀数组和后缀树是可以相互转化的,并且uoj上有大量通过后缀自动机建出后缀树然后dfs遍历获得后缀数组的模板,但是通过后缀数组来建后缀树的资料确实稀缺. 也许大牛们都觉得这xjbYY一下就可以写了,所以网上没找到对应的代码,那么我来补个坑吧.大牛勿喷.. 先谈谈我的理解吧.. 讲道理后缀数组和后缀树应该是完全等价的,但前两者