SPOJ_7258 SUBLEX

    这个题目可以先用O(N)的时间构造出后缀自动机,由于后缀自动机中每条路径都对应着一个不重复的子串,因此可以用dp处理出到达一个节点以及其后面的节点的路径数,然后每次查询时遍历一遍后缀自动机就可以了。

    但这样总的查询复杂度是O(N*Q)的,如果不用各种常数优化的话很容易TLE。

#include<stdio.h>
#include<string.h>
#define MAXD 90010
struct SufAuto
{
    int pre, next[26], len;
}sa[2 * MAXD];
namespace SA
{
    int node, tail;
    void init()
    {
        node = tail = 0;
        sa[0].pre = -1;
    }
    void insert(int k, int len)
    {
        int p = tail, np = ++ node;
        sa[np].len = len;
        for(; p != -1 && !sa[p].next[k]; p = sa[p].pre) sa[p].next[k] = np;
        tail = np;
        if(p == -1) sa[np].pre = 0;
        else
        {
            if(sa[sa[p].next[k]].len == sa[p].len + 1) sa[np].pre = sa[p].next[k];
            else
            {
                int q = sa[p].next[k], r = ++ node;
                sa[r] = sa[q], sa[r].len = sa[p].len + 1;
                sa[q].pre = sa[np].pre = r;
                for(; p != -1 && sa[p].next[k] == q; p = sa[p].pre) sa[p].next[k] = r;
            }
        }
    }
}
char b[MAXD];
int N, q[2 * MAXD], h[MAXD], f[2 * MAXD], num[2 * MAXD][26], next[2 * MAXD][26];
char ch[2 * MAXD][26];
void init()
{
    int i;
    SA::init();
    for(i = 0; b[i]; i ++) SA::insert(b[i] - 'a', i + 1);
    N = i;
}
void solve()
{
    int i, j, k, x, cur, t, rear, n;
    for(i = 0; i <= N; i ++) h[i] = 0;
    for(i = 1; i <= SA::node; i ++) ++ h[sa[i].len];
    for(i = 1; i <= N; i ++) h[i] += h[i - 1];
    for(i = SA::node; i >= 1; i --) q[-- h[sa[i].len]] = i;
    for(i = SA::node - 1; i >= 0; i --)
    {
        x = q[i];
        for(j = 0; j < 26; j ++) f[x] += f[sa[x].next[j]];
        ++ f[x];
    }
    for(i = 0; i <= SA::node; i ++)
        for(j = k = 0; j < 26; j ++)
            if(f[sa[i].next[j]])
                next[i][k] = sa[i].next[j], num[i][k] = f[sa[i].next[j]], ch[i][k] = j + 'a', ++ k;    
    scanf("%d", &t);
    while(t --)
    {
        scanf("%d", &n);
        for(i = k = 0; i < 26; i ++) k += num[0][i];
        n = (n - 1) % k + 1;
        rear = cur = 0;
        for(;;)
        {
            if(n == 0) break;
            for(i = 0; i < 26 && n > num[cur][i]; n -= num[cur][i ++]);
            -- n;
            b[rear ++] = ch[cur][i];
            cur = next[cur][i];
        }
        b[rear] = '\0';
        puts(b);
    }
}
int main()
{
    scanf("%s", b);
    init();
    solve();
    return 0;    
}

 

 

posted on 2012-09-11 16:04  Staginner  阅读(585)  评论(0编辑  收藏  举报