[SPOJ7258]SUBLEX - Lexicographical Substring Search

SUBLEX - Lexicographical Substring Search

Little Daniel loves to play with strings! He always finds different ways to have fun with strings! Knowing that, his friend Kinan decided to test his skills so he gave him a string S and asked him Q questions of the form:
If all distinct substrings of string S were sorted lexicographically, which one will be the K-th smallest?
After knowing the huge number of questions Kinan will ask, Daniel figured out that he can’t do this alone. Daniel, of course, knows your exceptional programming skills, so he asked you to write him a program which given S will answer Kinan’s questions.
Example:
S = “aaa” (without quotes)
substrings of S are “a” , “a” , “a” , “aa” , “aa” , “aaa”. The sorted list of substrings will be:
“a”, “aa”, “aaa”.
Input
In the first line there is Kinan’s string S (with length no more than 90000 characters). It contains only small letters of English alphabet. The second line contains a single integer Q (Q <= 500) , the number of questions Daniel will be asked. In the next Q lines a single integer K is given (0 < K < 2^31).
Output
Output consists of Q lines, the i-th contains a string which is the answer to the i-th asked question.
Example
Input:
aaa
2
2
3
Output:
aa
aaa

Solution
后缀自动机中,root出发到任意一个状态的路径对应一个子串,而且不重复
所以我们在自动机上预处理每一个状态的子串数目,然后从小到大枚举字符。
子串数目可以这样预处理出:s[x]=sum{s[y]}+1, y是x出发的下一个点,意思就是说,以x开头的子串有那么多个(即将孩子的所有子串前边都加上x),然后x单独算一个子串。
然后查找的时候从root出发(你可以这样想,因为root的right值包含了所有right,即root有所有后缀的r下标,所以只需要找最小的开头即可,然后转移后同理。
然后如果当前的转移的状态y,有s[y]>=k,那么说明子串在y的后缀里边,那么–k并且打印字符y后转移到y状态。(–k是因为我们转移的定义)
反之,如果s[y] < k, k-=s[y]

Code

#include <bits/stdc++.h>
using namespace std;

#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define MS(_) memset(_, 0, sizeof(_))
#define MP make_pair
#define PB push_back
inline void ckmax(int &x, int y) { if (x < y) x = y; }
inline void ckmin(int &x, int y) { if (x > y) x = y; }
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); }
    while (isdigit(ch))  { x = x * 10 + ch - '0'; ch = getchar(); }
    x *= f;
}

const int N = 180100;
char s[N];
int m;
int l[N], fa[N], nxt[N][26], d[N], loc[N], f[N]; 
struct sam{
    int root, last, cnt, len;
    sam() { cnt = 0; root = last = ++cnt; }
    inline void insert(int c){
        int np = ++cnt, p = last; last = np;
        l[np] = l[p] + 1;
        for (; p && !nxt[p][c]; nxt[p][c] = np, p = fa[p]);
        if (!p) fa[np] = root;
        else if (l[nxt[p][c]] == l[p]+1) fa[np] = nxt[p][c];
        else{
            int nq = ++cnt, q = nxt[p][c]; l[nq] = l[p]+1;
            memcpy(nxt[nq], nxt[q], sizeof nxt[q]); fa[nq] = fa[q];
            fa[q] = fa[np] = nq;
            for (; p && nxt[p][c] == q; nxt[p][c] = nq, p = fa[p]);
        }
    }
    inline void build(){
        scanf("%s", s+1); len = strlen(s+1);
        rep(i, 1, len) insert(s[i]-'a');
    }
    inline void topsort(){
        rep(i, 1, cnt) d[l[i]]++;
        rep(i, 1, len) d[i] += d[i-1];
        rep(i, 1, cnt) loc[d[l[i]]--] = i;
        per(i, cnt, 1){ int now = loc[i];
            f[now] = 1;
            rep(ch, 0, 25) f[now] += f[nxt[now][ch]];
        }
    }
    inline void query(){ int k, p;
        read(k);
        for (int p = root; k; )
            rep(ch, 0, 25) if (nxt[p][ch]){
                if (f[nxt[p][ch]] >= k){
                    p = nxt[p][ch]; putchar('a'+ch); 
                    k--; break;
                }else k -= f[nxt[p][ch]];
            }
        puts("");
    }
}SAM;

int main(){
    SAM.build();
    SAM.topsort();
    for (read(m); m; m--) SAM.query();
    return 0;
}

你可能感兴趣的:(后缀自动机)