Problem:
Problem H: Hth Number

Problem

長さ$N$の文字列$S$がある。$S$に含まれる文字はすべて0以上9以下の数字である。

$S$のすべての部分文字列から作られる数を全列挙してできた項数$\frac{N\times(N+1)}{2}$の数列を作った時、
その数列の中で$H$番目に小さい値を求めよ。

Input

入力は以下の形式で与えられる。

$N$ $H$
$S$

入力はすべて整数で与えられる。

1行目に$N$と$H$が空白区切りで与えられる。

2行目に長さ$N$の文字列$S$が与えられる。

Constraints

入力は以下の条件を満たす。

$1 \leq N \leq 10^5$

$1 \leq H \leq \frac{N \times (N+1)}{2}$

$|S| = N$

$S$に含まれる文字はすべて0, 1, 2, 3, 4, 5, 6, 7, 8, 9のいずれかである

Output

$H$番目の値を1行に出力せよ。

(出力の先頭に余計な0は含んではならない。)

Sample Input 1

2 3
00

Sample Output 1

0

Sample Input 2

4 9
0012

Sample Output 2

12

Sample Input 3

5 13
10031

Sample Output 3

100

Sample Input3の場合、10031の部分文字列から作られる数は以下の通りである。

1, 0, 0, 3, 1

10, 00, 03, 31

100, 003, 031

1003, 0031

10031

これらをを小さい順に並べると
0, 0, 0, 1, 1, 3, 3, 3, 10, 31, 31, 31, 100, 1003, 10031 になり、13番目に小さい値は100である。

Solution in C (judge verdict: Accepted)

#include<stdio.h>
#include<stdlib.h>
#define ll long long
//suffix arrayのO(NlogN)構築
int*makeSA(char*s,int len){
	len++;
	int*SAidx=calloc(len,sizeof(int));
	int*ord=calloc(len,sizeof(int));
	//1文字目
	int cnt1[256]={},cnt2[256]={};
	for(int i=0;i<len;i++)cnt1[s[i]+1]++,cnt2[s[i]+1]|=1;
	for(int i=1;i<256;i++)cnt1[i]+=cnt1[i-1],cnt2[i]+=cnt2[i-1];
	for(int i=0;i<len;i++)SAidx[cnt1[s[i]]++]=i,ord[i]=cnt2[s[i]];
	
	int*cnt=calloc(len+1,sizeof(int));
	int*idx2=calloc(len,sizeof(int));
	int*temp=calloc(len,sizeof(int));
	for(int k=1;k<len;k*=2){
		//2*k文字まで見る
		for(int i=0;i<len;i++)cnt[i]=0;
		for(int i=0;i<len;i++)cnt[ord[i]+1]++;
		for(int i=1;i<len;i++)cnt[i]+=cnt[i-1];
		for(int i=0;i<len;i++)idx2[cnt[ord[(SAidx[i]-k+len)%len]]++]=(SAidx[i]-k+len)%len;
		temp[idx2[0]]=0;
		for(int i=1;i<len;i++)temp[idx2[i]]=temp[idx2[i-1]]+(ord[idx2[i-1]]!=ord[idx2[i]]||ord[(idx2[i-1]+k)%len]!=ord[(idx2[i]+k)%len]);
		for(int i=0;i<len;i++)ord[i]=temp[i],SAidx[i]=idx2[i];
	}
	free(temp);
	free(idx2);
	free(cnt);
	free(ord);
	return SAidx+1;
}

char s[100010];
ll cnt[100010];
ll wei[100010];
int main(){
	ll n,k;
	scanf("%lld%lld %s",&n,&k,s);
	k--;
	ll crr=0;
	for(int i=0;i<n;i++){
		cnt[n-i]+=cnt[n-i+1];
		if(s[i]=='0'){
			cnt[0]+=crr+1;
			crr++;
		}else{
			cnt[n-i]+=crr+1;
			wei[i]=crr+1;
			crr=0;
		}
	}
	
	int keta=0;
	while(k>=cnt[keta])k-=cnt[keta++];
	if(keta==0){
		puts("0");
		return 0;
	}
	//keta桁で、小さい方からk番目
	int*p=makeSA(s,n);

	for(int i=0;i<n;i++){
		if(p[i]+keta<=n){
			k-=wei[p[i]];
			if(k<0){
				for(int j=0;j<keta;j++)putchar(s[p[i]+j]);puts("");
				return 0;
			}
		}
	}
}