codeforces128B——String

来源:互联网 发布:爱知中学高中部怎么样 编辑:程序博客网 时间:2024/06/10 15:47

1、题意:求一个字符串中所有子串按字典序从小到大排序后,第k小的子串是什么。
2、分析:这题好水这题思路简单,实现有一些细节。。
我们求出后缀数组,每一个后缀代表一些子串。要求第k小的,我们可以层层递进。先确定这个答案的第一个字符是什么,显然,最多才有26种字符,我们通过二分确定每一个字符对应的sa中的区间,然后判断第k小能否在这个区间里面。然后在确定第二个……

#include <map>#include <set>#include <cmath>#include <queue>#include <vector>#include <bitset>#include <string>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;#define M 200010#define LL long long#define MOD 1000000007#define inf 2147483647#define llinf 4000000000000000000ll#define For(i, x, y) for(int i = (x); i < (y); i ++)#define rep(i, x, y) for(int i = (x); i <= (y); i ++)#define drep(i, x, y) for(int i = (x); i >= (y); i --)inline int read(){    char ch=getchar();int x=0,f=1;    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}inline LL llread(){    char ch=getchar();LL x=0,f=1;    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}char ch[M];int c[M], x[M], y[M], sa[M];inline void getsa(){    int n = strlen(ch), m = 200;    ch[n ++] = '$';    For(i, 0, m) c[i] = 0;    For(i, 0, n) c[x[i] = ch[i]] ++;    For(i, 1, m) c[i] += c[i - 1];    drep(i, n - 1, 0) sa[-- c[x[i]]] = i;    for(int k = 1; k <= n; k <<= 1){        int tot = 0;        For(i, n - k, n) y[tot ++] = i;        For(i, 0, n) if(sa[i] >= k) y[tot ++] = sa[i] - k;        For(i, 0, m) c[i] = 0;        For(i, 0, n) c[x[y[i]]] ++;        For(i, 1, m) c[i] += c[i - 1];        drep(i, n - 1, 0) sa[-- c[x[y[i]]]] = y[i];        swap(x, y); tot = 1; x[sa[0]] = 0;        For(i, 1, n){            x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? tot - 1 : tot ++);        }        if(tot >= n) break; m = tot;     }//  For(i, 1, n) cout << sa[i] << endl;} LL qsum[M];inline LL sum(int l, int r, int o){    return qsum[r] - qsum[l - 1] - 1ll * o * (r - l + 1);}int main(){    //freopen("0input.in", "r", stdin);    scanf("%s", ch);    int len = strlen(ch); getsa();     rep(i, 1, len) qsum[i] = qsum[i - 1] + len - sa[i];     int now_rank = read(), l = 1, r = len;    if(now_rank > 1ll * len * (len + 1) >> 1ll) return puts("No such line."), 0;    rep(i, 0, inf){        int left = l, right;        //if(ch[sa[0] + i] == '$') left ++;        rep(j, 'a', 'z'){            int ll = l, rr = r, ans = -1;            while(ll <= rr){                int mid = (ll + rr) / 2;                if(ch[sa[mid] + i] <= j) ll = (ans = mid) + 1;                else rr = mid - 1;            }            if(ans == -1) continue;            if(ch[sa[ans] + i] == 'z' + 1) left ++;             if(ch[sa[ans] + i] != j) continue;            right = ans;            LL qt = sum(left, right, i);            if(1 <= now_rank && now_rank <= right - left + 1){                return printf("%c", (char)(j)), 0;            }            if(1ll * now_rank <= qt){                printf("%c", (char)(j));                l = left, r = right;                now_rank -= (r - l + 1);                if(ch[sa[l] + i + 1] == '$') l ++;                break;            }            //cout << qt << " " << now_rank << endl;             now_rank -= qt;            left = ans + 1;        }    }     return 0;}
1 0