Problem:
Kth Sentence
A student, Kita_masa, is taking an English examination.
In this examination, he has to write a sentence of length
m
.
Since he completely forgot the English grammar, he decided to consider all sentences of length
m
constructed by concatenating the words he knows and write the
K
-th sentence among the candidates sorted in lexicographic order.
He believes that it must be the correct sentence because
K
is today's lucky number for him.
Each word may be used multiple times (or it's also fine not to use it at all) and the sentence does not contain any extra character between words.
Two sentences are considered different if the order of the words are different even if the concatenation resulted in the same string.
Input
The first line contains three integers
n
(
1 \leq n \leq 100
),
m
(
1 \leq m \leq 2000
) and
K
(
1 \leq K \leq 10^{18}
), separated by a single space.
Each of the following
n
lines contains a word that Kita_masa knows.
The length of each word is between 1 and 200, inclusive, and the words contain only lowercase letters.
You may assume all the words given are distinct.
Output
Print the
K
-th (1-based) sentence of length
m
in lexicographic order.
If there is no such a sentence, print "-".
Sample Input 1
2 10 2
hello
world
Output for the Sample Input 1
helloworld
Sample Input 2
3 3 6
a
aa
b
Output for the Sample Input 2
aba
Sample Input 3
2 59 1000000000000000000
a
b
Output for the Sample Input 3
-

Submitted solutions:

code_snippet: #include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <iostream>
#include <cstdlib>
#include <sstream>

using namespace std;

const long long oo=1000000000000000001LL;
void add(long long &a,long long b)
{
	a+=b;
	if(a>=oo)a=oo;
}
long long mult(long long a,long long b)
{
	if(a==0||b==0)return 0;
	if(a<=oo/b)return a*b;
	return oo;
}

int n;
char a[110][220];
int len[110];

long long dp[2010];
long long f[2010];

bool can[110][220];
long long cnt[30];

int main()
{
	int m;
	long long K;
	while(~scanf("%d %d %lld",&n,&m,&K))
	{
		for(int i=1;i<=n;i++)scanf("%s",a[i]+1);
		for(int i=1;i<=n;i++)len[i]=strlen(a[i]+1);
		dp[0]=1;
		for(int i=1;i<=m;i++)
		{
			dp[i]=0;
			for(int j=1;j<=n;j++)
				if(len[j]<=i)
					add(dp[i],dp[i-len[j]]);
		}
		if(K>dp[m])
		{
			puts("-");
			continue;
		}
		memset(can,0,sizeof(can));
		for(int i=1;i<=n;i++)can[i][0]=1;
		memset(f,0,sizeof(f));
		f[0]=1;

		for(int i=1;i<=m;i++)
		{
			memset(cnt,0,sizeof(cnt));
			for(int j=1;j<=n;j++)
				for(int k=0;k<len[j];k++)
					if(can[j][k])
					{
						int ll=m-i-(len[j]-(k+1));
						if(ll<0)continue;
						long long t=mult(f[i-k-1],dp[ll]);
						add(cnt[a[j][k+1]-'a'],t);
					}
			for(int x=0;x<26;x++)
			{
				if(cnt[x]<K)
					K-=cnt[x];
				else
				{
					printf("%c",'a'+x);
					for(int j=1;j<=n;j++)
						if('a'+x==a[j][len[j]]&&can[j][len[j]-1])
							add(f[i],f[i-len[j]]);
					for(int j=1;j<=n;j++)
					{
						for(int k=len[j]-1;k>=1;k--)
							if(can[j][k-1]&&('a'+x)==a[j][k])
								can[j][k]=1;
							else can[j][k]=0;
					}
					break;
				}
			}
		}

		puts("");
	}
}
accuracy: 1
time_ms: 240
memory_kb: 3556
final_rank: 1

code_snippet: #include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

int top[110];
char ch[110][210];
int pn[110];
char ans[2010];
long long num[2010];
int N, M;
long long K;
int st['z'+1][110][210];
int tp['z'+1][110];
long long oo;
int S[110][210];

int main() {
    // oo ~ 1e18 + 1, used as cap
    oo = 1;
    for (int i = 0; i < 18; i++) oo *= 10;
    oo += 1;

    // read input
    if (scanf("%d %d %lld", &N, &M, &K) != 3) return 0;
    for (int i = 0; i < N; i++) {
        scanf("%s", ch[i]);
        pn[i] = strlen(ch[i]);
    }

    // build st and tp: positions of each char j in each word i
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < pn[i]; j++) {
            unsigned char c = ch[i][j];
            st[c][i][ tp[c][i]++ ] = j;
        }
    }

    // DP count: num[len] = number of length-len sentences (capped at oo)
    memset(num, 0, sizeof(num));
    num[0] = 1;
    for (int i = 1; i <= M; i++) {
        for (int j = 0; j < N; j++) {
            if (i >= pn[j]) {
                num[i] += num[i - pn[j]];
                if (num[i] > oo) num[i] = oo;
            }
        }
    }

    // initial state
    long long last = 1;  // count of suffix completions ending at previous position

    // build answer character by character
    for (int i = 0; i < M; i++) {
        char pick = 0;
        for (int jc = 'a'; jc <= 'z'; jc++) {
            long long s = 0;
            // count how many sentences start with prefix ans[0..i-1] + jc
            for (int k = 0; k < N; k++) {
                for (int o = 0; o < tp[jc][k]; o++) {
                    int t = st[jc][k][o];
                    if (t > 0) {
                        // only if we have a previous char to match
                        if (i > 0 && ch[k][t-1] == ans[i-1] && M - i >= pn[k] - t) {
                            __int128 tmp = ( __int128 ) S[k][t-1] * num[M - i - (pn[k] - t)];
                            long long piece = tmp > oo ? oo : (long long) tmp;
                            s += piece;
                            if (s > oo) s = oo;
                        }
                    } else {
                        // t == 0: word starts here
                        if (M - i >= pn[k]) {
                            __int128 tmp = ( __int128 ) last * num[M - i - pn[k]];
                            long long piece = tmp > oo ? oo : (long long) tmp;
                            s += piece;
                            if (s > oo) s = oo;
                        }
                    }
                }
            }
            if (K > s) {
                K -= s;
            } else {
                pick = jc;
                break;
            }
        }
        if (!pick) {
            // no character can satisfy K
            printf("-\n");
            return 0;
        }

        ans[i] = pick;

        // update S[k][o]: counts of partial matches ending at this character
        for (int k = 0; k < N; k++) {
            for (int o = pn[k] - 1; o > 0; o--) {
                if (i > 0 && ch[k][o] == pick && ch[k][o-1] == ans[i-1]) {
                    S[k][o] = S[k][o-1];
                } else {
                    S[k][o] = 0;
                }
            }
            S[k][0] = (ch[k][0] == pick ? last : 0);
        }

        // recompute last = count of sentences whose last word ended exactly at pos i
        last = 0;
        for (int k = 0; k < N; k++) {
            if (ch[k][ pn[k] - 1 ] == pick) {
                last += S[k][ pn[k] - 1 ];
                if (last > oo) last = oo;
            }
        }
    }

    ans[M] = '\0';
    printf("%s\n", ans);
    return 0;
}
accuracy: 0.7872340425531915
time_ms: 50
memory_kb: 6544
final_rank: 19

code_snippet: #include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int top[110];
char ch[110][210];
int pn[110];
char ans[2010];
long long num[2010];
int N,M;long long K;
int st['z'+1][110][210];
int tp['z'+1][110];
long long oo;
int S[110][210];
int main()
{
    int i,j,k,o;oo=1;for(i=0;i<18;i++)oo*=10;oo+=1;
    scanf("%d%d%lld",&N,&M,&K);
    for(i=0;i<N;i++)
    {
        scanf("%s",ch[i]);
        pn[i]=strlen(ch[i]);
    }

    for(i=0;i<N;i++)
    {
        for(j=0;j<pn[i];j++)
        {
            st[ch[i][j]][i][tp[ch[i][j]][i]++]=j;
        }
    }

    num[0]=1;
    for(i=1;i<=M;i++)
    {
        for(j=0;j<N;j++)
        {
            if(i>=pn[j]) num[i]+=num[i-pn[j]];
            if(num[i]>oo) num[i]=oo;
        }
    }
    for(i=0;i<N;i++) top[i]=-1;
    long long s=0;
    long long last=1;
    int t;
    for(i=0;i<M;i++)
    {
        for(j='a';j<='z';j++)
        {
            s=0;
            for(k=0;k<N;k++)
            {
                for(o=0;o<tp[j][k];o++)
                {
                    t=st[j][k][o];
                    if(t>0)
                    {
                        if(ch[k][t-1]==ans[i-1]&&M-i>=pn[k]-t)
                        {
                            if(S[k][t-1]*(double)(num[M-i-(pn[k]-t)])>oo*2) s=oo;
                            else
                            {
                                s+=S[k][t-1]*num[M-i-(pn[k]-t)];
                                if(s>oo) s=oo;
                            }
                        }
                    }
                    else
                    {
                        if(M-i>=pn[k]-t)
                        {
                            if(last*(double)(num[M-i-(pn[k]-t)])>oo*2) s=oo;
                            else
                            {
                                s+=last*num[M-i-(pn[k]-t)];
                                if(s>oo) s=oo;
                            }
                        }
                    }
                }
            }
            if(K>s) K-=s;
            else break;
        }
        if(j>'z')
        {
            printf("-\n");
            return 0;
        }
        ans[i]=j;
        for(k=0;k<N;k++)
        {
            for(o=pn[k]-1;o>0;o--)
            {
                if(i>0&&ch[k][o]==j&&ch[k][o-1]==ans[i-1])
                {
                    S[k][o]=S[k][o-1];
                }
                else S[k][o]=0;
            }
        }
        for(k=0;k<N;k++)
        {
            if(ch[k][0]==j) S[k][0]=last;
            else S[k][0]=0;
        }
        last=0;
        for(k=0;k<N;k++)
        {
            if(ch[k][pn[k]-1]==j)
            {
                last+=S[k][pn[k]-1];
                if(last>oo) last=oo;
            }
        }
    }
    ans[M]=0;
    printf("%s\n",ans);
    return 0;
}
accuracy: 0.7872340425531915
time_ms: 50
memory_kb: 6444
final_rank: 18

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

string solve(int N, int M, long long K, const vector<string>& S) {
	// step #1. construct trie
	array<int, 26> empty;
	fill(empty.begin(), empty.end(), -1);
	vector<array<int, 26> > trie;
	trie.push_back(empty);
	vector<bool> terminal = { false };
	for (int i = 0; i < N; i++) {
		int pos = 0;
		for (char c : S[i]) {
			int id = int(c - 'a');
			if (trie[pos][id] == -1) {
				trie[pos][id] = trie.size();
				trie.push_back(empty);
				terminal.push_back(false);
			}
			pos = trie[pos][id];
		}
		terminal[pos] = true;
	}
	int Z = trie.size();

	// step #2. dynamic programming
	const int BUCKET = (M + 2) / 3;
	vector<vector<long long> > dp(BUCKET);
	int leftpos = M + 1;
	auto calc_dp = [&](int pos) {
		// calculate DP of positions [pos, pos + BUCKET)
		leftpos = pos;
		vector<long long> subdp(Z, 0), ndp(Z, 0);
		for (int i = 0; i < Z; i++) {
			if (terminal[i]) {
				subdp[i] = 1;
			}
		}
		if (pos <= M && M < pos + BUCKET) {
			dp[M - pos] = subdp;
		}
		for (int i = M - 1; i >= pos; i--) {
			fill(ndp.begin(), ndp.end(), 0);
			for (int j = 0; j < Z; j++) {
				for (int k = 0; k < 26; k++) {
					if (trie[j][k] != -1) {
						ndp[j] += subdp[trie[j][k]];
						ndp[j] = min(ndp[j], K + 1);
					}
				}
				if (terminal[j]) {
					for (int k = 0; k < 26; k++) {
						if (trie[0][k] != -1) {
							ndp[j] += subdp[trie[0][k]];
							ndp[j] = min(ndp[j], K + 1);
						}
					}
				}
			}
			subdp = ndp;
			if (i < pos + BUCKET) {
				dp[i - pos] = subdp;
			}
		}
	};

	// step #3. if the number is less than K...
	const int DIVISIONS = 5; // to decrease memory, separate calculation of DP
	int curdiv = 0;
	auto get = [&](int a, int b) -> long long {
		if (a < leftpos || a >= leftpos + BUCKET) {
			calc_dp(a);
		}
		return dp[a - leftpos][b];
	};
	if (get(0, 0) < K) {
		return string();
	}
	
	// step #4. reconstruction of answer
	vector<long long> v(Z, 0), nv(Z, 0);
	v[0] = 1;
	string ans = "";
	long long rem = K;
	for (int i = 0; i < M; i++) {
		vector<long long> states(26, 0);
		for (int j = 0; j < Z; j++) {
			for (int k = 0; k < 26; k++) {
				if (trie[j][k] != -1) {
					double g = double(v[j]) * get(i + 1, trie[j][k]);
					states[k] += (g < 1.01 * (K + 1) ? v[j] * get(i + 1, trie[j][k]) : K + 1);
					states[k] = min(states[k], K + 1);
				}
			}
			if (terminal[j]) {
				for (int k = 0; k < 26; k++) {
					if (trie[0][k] != -1) {
						double g = double(v[j]) * get(i + 1, trie[0][k]);
						states[k] += (g < 1.01 * (K + 1) ? v[j] * get(i + 1, trie[0][k]) : K + 1);
						states[k] = min(states[k], K + 1);
					}
				}
			}
		}
		for (int j = 0; j < 26; j++) {
			if (rem <= states[j]) {
				ans += char('a' + j);
				fill(nv.begin(), nv.end(), 0);
				for (int k = 0; k < Z; k++) {
					if (trie[k][j] != -1) {
						nv[trie[k][j]] += v[k];
						nv[trie[k][j]] = min(nv[trie[k][j]], K + 1);
					}
					if (terminal[k] && trie[0][j] != -1) {
						nv[trie[0][j]] += v[k];
						nv[trie[0][j]] = min(nv[trie[0][j]], K + 1);
					}
				}
				v = nv;
				break;
			}
			rem -= states[j];
		}
	}

	return ans;
}

int main() {
	int N, M; long long K;
	cin >> N >> M >> K;
	vector<string> S(N);
	for (int i = 0; i < N; i++) {
		cin >> S[i];
	}
	string ans = solve(N, M, K, S);
	cout << (ans != string() ? ans : string("-")) << endl;
	return 0;
}
accuracy: 1
time_ms: 2380
memory_kb: 109256
final_rank: 13

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return true; } return false; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return true; } return false; }
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define mp make_pair
#define si(x) int(x.size())
const int mod1=998244353,mod2=999999937,MAX=105;
const ll INF=1LL<<61;

struct Rollinghash{
    string S;
    int n;
    int base1;
    int base2;
    vector<ll> h1,h2,ru1,ru2;
    
    void make(string &T,int ba1,int ba2){
        S=T;
        n=S.size();
        h1.assign(n+1,0);
        h2.assign(n+1,0);
        ru1.assign(n+1,0);
        ru2.assign(n+1,0);
        base1=ba1;
        base2=ba2;
        
        ru1[0]=1;
        ru2[0]=1;
        
        for(int i=1;i<=n;i++){
            h1[i]=h1[i-1]*base1+ll(S[i-1]-'A');
            h1[i]%=mod1;
            
            h2[i]=h2[i-1]*base2+ll(S[i-1]-'A');
            h2[i]%=mod2;
            
            ru1[i]=ru1[i-1]*base1%mod1;
            ru2[i]=ru2[i-1]*base2%mod2;
        }
    }
    
    pair<ll,ll> ha(int l,int r){
        pair<ll,ll> res;
        res.fi=(h1[r]-h1[l]*ru1[r-l]%mod1+mod1)%mod1;
        res.se=(h2[r]-h2[l]*ru2[r-l]%mod2+mod2)%mod2;
        
        return res;
    }//開区間
};


ll pat[2005];
ll dp[2005];

int main(){
    
    std::ifstream in("text.txt");
    std::cin.rdbuf(in.rdbuf());
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    ll ha1=37,ha2=43;
    
    ll N,M,K;cin>>N>>M>>K;
    vector<string> S(N);
    for(int i=0;i<N;i++) cin>>S[i];
    
    vector<Rollinghash> roro(N);
    for(int i=0;i<N;i++){
        roro[i].make(S[i],ha1,ha2);
    }
    
    pat[0]=1;
    for(int i=0;i<M;i++){
        for(int j=0;j<N;j++){
            if(i+si(S[j])<=M){
                pat[i+si(S[j])]+=pat[i];
                chmin(pat[i+si(S[j])],INF);
            }
        }
    }
    
    if(pat[M]<K){
        cout<<"-"<<endl;
        return 0;
    }
    
    dp[0]=1;
    
    string ans;
    for(int s=1;s<=M;s++){
        Rollinghash ro;
        ro.make(ans,ha1,ha2);
        char left=-1,right=25;
        while(right-left>1){
            int mid=(left+right)/2;
            char cc=char('a'+mid);
            ans+=cc;
            
            ll sum=0;
            for(int i=0;i<N;i++){
                for(int j=0;j<si(S[i]);j++){
                    int mae=si(S[i])-j;
                    if(s-mae<0) continue;
                    if(s+j>M) continue;
                    if(ro.ha(s-mae,s-1)==roro[i].ha(0,mae-1)&&ans.back()>=S[i][mae-1]){//ans.substr(s-mae,mae-1)==S[i].substr(0,mae-1)&&ans.back()>=S[i][mae-1]){
                        ll tasu;
                        if(pat[M-(s+j)]==0||dp[s-mae]<=INF/pat[M-(s+j)]){
                            tasu=dp[s-mae]*pat[M-(s+j)];
                        }else{
                            tasu=INF;
                        }
                        sum+=tasu;
                        chmin(sum,INF);
                    }
                }
            }
            
            if(sum>=K) right=mid;
            else left=mid;
            
            ans.pop_back();
        }
        
        ans+=char('a'+right);
        
        ll sum=0;
        for(int i=0;i<N;i++){
            for(int j=0;j<si(S[i]);j++){
                int mae=si(S[i])-j;
                if(s-mae<0) continue;
                if(s+j>M) continue;
                if(ro.ha(s-mae,s-1)==roro[i].ha(0,mae-1)&&ans.back()>S[i][mae-1]){//ans.substr(s-mae,mae-1)==S[i].substr(0,mae-1)&&ans.back()>=S[i][mae-1]){
                    ll tasu;
                    if(pat[M-(s+j)]==0||dp[s-mae]<=INF/pat[M-(s+j)]){
                        tasu=dp[s-mae]*pat[M-(s+j)];
                    }else{
                        tasu=INF;
                    }
                    sum+=tasu;
                    chmin(sum,INF);
                }
            }
        }
        K-=sum;
        for(int i=0;i<N;i++){
            if(s>=si(S[i])&&ans.substr(s-si(S[i]))==S[i]){
                dp[s]+=dp[s-si(S[i])];
                chmin(dp[s],INF);
            }
        }
    }
    
    cout<<ans<<endl;
}
accuracy: 1
time_ms: 2140
memory_kb: 4408
final_rank: 10

code_snippet: #include <bits/stdc++.h>

using namespace std;

constexpr long long inf = 2e18;

long long add(long long x, long long y) {
    return min(x + y, inf);
}
long long mul(long long x, long long y) {
    return min(__int128_t(inf), __int128_t(x) * y);
}

int main() {
    int n, m;
    long long k;
    cin >> n >> m >> k;
    vector<string> w(n);
    for (auto& s : w) cin >> s;

    const int l = [&w] {
        int l = 0;
        for (const auto& s : w) l = max<int>(l, s.size());
        return l;
    }();

    vector<long long> f(m + 1);
    f[0] = 1;
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) if (int k = i + int(w[j].size()); k <= m) {
            f[k] = add(f[k], f[i]);
        }
    }
    if (f[m] < k) {
        cout << "-" << endl;
        return 0;
    }

    string ans;

    vector<long long> cnt(m + 1);
    cnt[0] = 1;

    vector<set<int>> match(m + 1);
    for (int i = 0; i < m; ++i) for (int k = 0; k < n; ++k) {
        int len = i + int(w[k].size());
        if (len <= m) {
            match[i].insert(k);
        }
    }

    for (int i = 0; i < m; ++i) {
        for (int j = max(0, i - l + 1); j < i; ++j) {
            for (auto it = match[j].begin(); it != match[j].end();) {
                int k = *it;
                int len = j + int(w[k].size());
                if (len <= i or len > m or w[k][i - 1 - j] != ans[i - 1]) {
                    it = match[j].erase(it);
                } else {
                    ++it;
                }
            }
        }

        vector<long long> sum(26);
        for (int j = max(0, i - l + 1); j <= i; ++j) {
            for (int k : match[j]) {
                int len = j + int(w[k].size());
                int idx = w[k][i - j] - 'a';
                sum[idx] = add(sum[idx], mul(cnt[j], f[m - len]));
            }
        }

        char x = '\0';
        for (int i = 0; i < 26; ++i) {
            if (sum[i] >= k) {
                x = 'a' + i;
                break;
            }
            k -= sum[i];
        }
        assert(x);
        ans += x;

        for (int k = 0; k < n; ++k) {
            const int j = i + 1 - int(w[k].size());
            if (j < 0 or j > m) continue;
            if (w[k].back() != x) continue;
            if (auto it = match[j].find(k); it == match[j].end()) continue;
            cnt[i + 1] = add(cnt[i + 1], cnt[j]);
        }
    }
    cout << ans << endl;

    return 0;
}
accuracy: 1
time_ms: 320
memory_kb: 12684
final_rank: 5

code_snippet: #include <bits/stdc++.h>

using namespace std;

constexpr long long inf = 2e18;

long long add(long long x, long long y) {
    return min(x + y, inf);
}
long long mul(long long x, long long y) {
    return min(__int128_t(inf), __int128_t(x) * y);
}

int main() {
    int n, m;
    long long k;
    cin >> n >> m >> k;
    vector<string> w(n);
    for (auto& s : w) cin >> s;

    const int l = [&w] {
        int l = 0;
        for (const auto& s : w) l = max<int>(l, s.size());
        return l;
    }();

    vector<long long> f(m + 1);
    f[0] = 1;
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) if (int k = i + int(w[j].size()); k <= m) {
            f[k] = add(f[k], f[i]);
        }
    }
    if (f[m] < k) {
        cout << "-" << endl;
        return 0;
    }

    string ans;

    vector<long long> cnt(m + 1);
    cnt[0] = 1;

    vector<set<int>> match(m + 1);
    for (int i = 0; i < m; ++i) for (int k = 0; k < n; ++k) {
        int len = i + int(w[k].size());
        if (len <= m) {
            match[i].insert(k);
        }
    }

    for (int i = 0; i < m; ++i) {
        for (int j = max(0, i - l + 1); j < i; ++j) {
            for (auto it = match[j].begin(); it != match[j].end();) {
                int k = *it;
                int len = j + int(w[k].size());
                if (len <= i or len > m or w[k][i - 1 - j] != ans[i - 1]) {
                    it = match[j].erase(it);
                } else {
                    ++it;
                }
            }
        }

        vector<long long> sum(26);
        for (int j = max(0, i - l + 1); j <= i; ++j) {
            for (int k : match[j]) {
                int len = j + int(w[k].size());
                int idx = w[k][i - j] - 'a';
                sum[idx] = add(sum[idx], mul(cnt[j], f[m - len]));
            }
        }

        char x = '\0';
        for (int i = 0; i < 26; ++i) {
            if (sum[i] >= k) {
                x = 'a' + i;
                break;
            }
            k -= sum[i];
        }
        assert(x);
        ans += x;

        for (int k = 0; k < n; ++k) {
            const int j = i + 1 - int(w[k].size());
            if (w[k].back() != x) continue;
            // ans[j:i] = w[k][:i-j]
            bool ok = true;
            for (int p = 0; p < i - j; ++p) {
                ok &= ans[j + p] == w[k][p];
            }
            if (ok) {
                cnt[i + 1] = add(cnt[i + 1], cnt[j]);
            }
        }
    }
    cout << ans << endl;

    return 0;
}
accuracy: 1
time_ms: 330
memory_kb: 12840
final_rank: 6

code_snippet: #include <bits/stdc++.h>

using namespace std;

constexpr long long inf = 2e18;

long long add(long long x, long long y) {
    return min(x + y, inf);
}
long long mul(long long x, long long y) {
    return min(__int128_t(inf), __int128_t(x) * y);
}

int main() {
    int n, m;
    long long k;
    cin >> n >> m >> k;
    vector<string> w(n);
    for (auto& s : w) cin >> s;

    const int l = [&w] {
        int l = 0;
        for (const auto& s : w) l = max<int>(l, s.size());
        return l;
    }();

    vector<long long> f(m + 1);
    f[0] = 1;
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) if (int k = i + int(w[j].size()); k <= m) {
            f[k] = add(f[k], f[i]);
        }
    }
    if (f[m] < k) {
        cout << "-" << endl;
        return 0;
    }

    string ans;

    vector<long long> cnt(m + 1);
    cnt[0] = 1;

    vector<set<int>> match(m + 1);
    for (int i = 0; i < m; ++i) for (int k = 0; k < n; ++k) {
        match[i].insert(k);
    }

    for (int i = 0; i < m; ++i) {
        for (int j = max(0, i - l + 1); j < i; ++j) {
            for (auto it = match[j].begin(); it != match[j].end();) {
                int k = *it;
                int len = j + int(w[k].size());
                if (len <= i or len > m or w[k][i - 1 - j] != ans[i - 1]) {
                    it = match[j].erase(it);
                } else {
                    ++it;
                }
            }
        }

        auto count_leq = [&](char x) -> long long {
            long long res = 0;
            for (int j = max(0, i - l + 1); j <= i; ++j) {
                for (int k : match[j]) {
                    int len = j + int(w[k].size());
                    if (len <= i or len > m or w[k][i - j] > x) continue;
                    // ans[j:i] = w[k][:i-j]
                    res = add(res, mul(cnt[j], f[m - len]));
                }
            }
            return res;
        };

        long long sub = 0;
        char lc = 'a' - 1, rc = 'z' + 1;
        while (rc - lc > 1) {
            char x = (lc + rc) / 2;
            long long c = count_leq(x);
            if (c >= k) {
                rc = x;
            } else {
                lc = x;
                sub = c;
            }
        }
        k -= sub;

        char x = rc;
        ans += x;

        for (int k = 0; k < n; ++k) {
            const int j = i + 1 - int(w[k].size());
            if (w[k].back() != x) continue;
            // ans[j:i] = w[k][:i-j]
            bool ok = true;
            for (int p = 0; p < i - j; ++p) {
                ok &= ans[j + p] == w[k][p];
            }
            if (ok) {
                cnt[i + 1] = add(cnt[i + 1], cnt[j]);
            }
        }
    }
    cout << ans << endl;

    return 0;
}
accuracy: 1
time_ms: 850
memory_kb: 12976
final_rank: 8

code_snippet: //#pragma GCC optimize("O3")
//#pragma GCC optimize("unroll-loops")
#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
#include<array>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
const ll mod = 1000000007;
const ll INF = mod * mod;
typedef pair<int, int>P;

#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;
typedef long double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-8;
const ld pi = acosl(-1.0);

ll mod_pow(ll x, ll n, ll m = mod) {
	if (n < 0) {
		ll res = mod_pow(x, -n, m);
		return mod_pow(res, m - 2, m);
	}
	if (abs(x) >= m)x %= m;
	if (x < 0)x += m;
	ll res = 1;
	while (n) {
		if (n & 1)res = res * x % m;
		x = x * x % m; n >>= 1;
	}
	return res;
}
struct modint {
	ll n;
	modint() :n(0) { ; }
	modint(ll m) :n(m) {
		if (n >= mod)n %= mod;
		else if (n < 0)n = (n % mod + mod) % mod;
	}
	operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
	if (n == 0)return modint(1);
	modint res = (a * a) ^ (n / 2);
	if (n % 2)res = res * a;
	return res;
}

ll inv(ll a, ll p) {
	return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }
modint operator/=(modint& a, modint b) { a = a / b; return a; }
const int max_n = 1 << 20;
modint fact[max_n], factinv[max_n];
void init_f() {
	fact[0] = modint(1);
	for (int i = 0; i < max_n - 1; i++) {
		fact[i + 1] = fact[i] * modint(i + 1);
	}
	factinv[max_n - 1] = modint(1) / fact[max_n - 1];
	for (int i = max_n - 2; i >= 0; i--) {
		factinv[i] = factinv[i + 1] * modint(i + 1);
	}
}
modint comb(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[b] * factinv[a - b];
}
modint combP(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[a - b];
}
int gcd(int a, int b) {
	if (a < b)swap(a, b);
	while (b) {
		int r = a % b; a = b; b = r;
	}
	return a;
}

ll sup = 2 * INF;
void ad(ll& a, ll b) {
	a += b; a = min(a, sup);
}
ll mul(ll a, ll b) {
	if (a == 0)return 0;
	if (sup / a < b)return sup;
	return a * b;
}

bool canuse[2005][105];
void solve() {
	int n, m; ll k; cin >> n >> m >> k; k--;
	vector<string> s(n);
	rep(i, n)cin >> s[i];
	vector<ll> dp(m + 1);
	dp[0] = 1;
	rep(i, m) {
		rep(j, n) {
			int ni = i + s[j].size();
			if (ni <= m) {
				ad(dp[ni], dp[i]);
			}
		}
	}
	if (k >= dp[m]) {
		cout << "-\n"; return;
	}
	vector<ll> curdp(m + 1);
	curdp[0] = 1;
	rep(j, m)rep(i, n) {
		if (j + s[i].size() <= m)canuse[j][i] = true;
		else canuse[j][i] = false;
	}
	string ans;
	rep(i, m) {
		vector<ll> nums(26);
		rep(k, n) {
			rep(dec, s[k].size()) {
				int j = i - dec;
				if (j < 0)continue;
				if (canuse[j][k]) {
					int c = s[k][dec] - 'a';
					ad(nums[c], mul(curdp[j], dp[m - (j + s[k].size())]));
				}
			}
		}
		int chk = -1;
		rep(c, 26) {
			if (k >= nums[c]) {
				k -= nums[c];
			}
			else {
				chk = c; break;
			}
		}
		ans.push_back('a' + chk);
		rep(k, n) {
			rep(dec, s[k].size()) {
				int j = i - dec;
				if (j < 0)continue;
				int c = s[k][dec] - 'a';
				if (c != chk)canuse[j][k] = false;
				if (dec == s[k].size() - 1) {
					if (canuse[j][k]) {
						ad(curdp[i + 1], curdp[j]);
					}
				}
			}
		}
	}
	cout << ans << "\n";
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	//cout << fixed << setprecision(10);
	//init_f();
	//init();
	//int t; cin >> t; rep(i, t)
	//while (cin>>n>>m>>r,n)
		solve();
	return 0;
}
accuracy: 1
time_ms: 220
memory_kb: 20112
final_rank: 7

code_snippet: #include <algorithm>
#include <cassert>
#include <climits>
#include <cmath>
#include <iostream>
#include <iterator>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <random>
#include <complex>
#include <bitset>
#include <iomanip>
#include <memory>
#include <functional>

#define rep(i, n, s) for (int i = (s); i < int(n); i++)
#define per(i, n, s) for (int i = (n - 1); i >= int(s); i--)
#define MM << " " <<
#define all(x) x.begin(), x.end()
#define rall(x) rbegin(x), rend(x)

template <class T>
using MinHeap = std::priority_queue<T, std::vector<T>, std::greater<T>>;
template <class T>
using MaxHeap = std::priority_queue<T>;

using ll = long long;
using Pii = std::pair<int, int>;
using Pll = std::pair<ll, ll>;
using Pdd = std::pair<double, double>;

template <class T>
bool chmin(T &a, const T b) {
    if (a > b) {
        a = b;
        return true;
    } else {
        return false;
    }
}

template <class T>
bool chmax(T &a, const T b) {
    if (a < b) {
        a = b;
        return true;
    } else {
        return false;
    }
}

template <class T>
void vdeb(const std::vector<T> &da) {
    auto n = da.size();
    for (size_t i = 0; i < n; i++) {
        if (i == n - 1)
            std::cout << da[i];
        else
            std::cout << da[i] << " ";
    }
    std::cout << std::endl;
}
template<class T>
void vdeb(const std::vector<std::vector<T>> &da) {
    auto n = da.size();
    for (size_t i = 0; i < n; i++) {
        // std::cout << i << " : ";
        vdeb(da[i]);
    }
    std::cout << std::endl;
}

template <>
void vdeb(const std::vector<std::string> &da) {
    auto n = da.size();
    for (size_t i = 0; i < n; i++) { std::cout << da[i] << std::endl; }
}

using namespace std;

const size_t MAX_K = 1ULL<<61;

struct DP {
    size_t i, j, c;
    DP(size_t _i, size_t _j, size_t _c)
    : i(_i), j(_j), c(_c) {}
    DP() {}
};

int main() {
    size_t n, m, k; cin >> n >> m >> k;
    vector<string> da(n);
    rep(i,n,0) cin >> da[i];
    vector<size_t> dp(m + 1);
    dp[0]++;
    auto plus = [](size_t &x, size_t y, size_t z = 1) {
        if(z > 0 && (MAX_K*2 - x) / z <= y) x = MAX_K;
        else {
            x += y * z;
            chmin(x, MAX_K);
        }
    };
    rep(j,m,0) {
        rep(i,n,0) {
            if(j + da[i].size() <= m) {
                plus(dp[j + da[i].size()], dp[j]);
            }
        }
    }
    if(dp.back() < k) {
        cout << "-" << endl;
        return 0;
    }
    reverse(all(dp));
    size_t cnt = 1;
    vector<DP> nxt;
    string ans;
    // vdeb(dp);
    rep(i,m,0) {
        vector<size_t> predictor(26);
        rep(j,n,0) {
            if(i + da[j].size() <= m) plus(predictor[da[j][0] - 'a'], dp[i + da[j].size()], cnt);
        }
        for(auto &now : nxt) {
            auto idx = i + (da[now.i].size() - now.j);
            // cerr << now.i MM now.j MM da[now.i][now.j]-'a' << endl;
            if(idx <= m) plus(predictor[da[now.i][now.j]-'a'], dp[idx], now.c);
        }
        // vdeb(predictor);
        vector<DP> tmp;
        size_t nc = 0;
        rep(j,26,0) {
            if(predictor[j] < k) {
                k -= predictor[j];
            } else {
                ans += j + 'a';
                rep(l,n,0) {
                    if(da[l][0] == j + 'a') {
                        if(da[l].size() > 1) tmp.push_back({size_t(l), 1, cnt});
                        else plus(nc, cnt);
                    }
                }
                for(auto &now : nxt) {
                    if(da[now.i][now.j] != j + 'a') continue;
                    if(da[now.i].size() == now.j+1) {
                        plus(nc, now.c);
                    } else {
                        now.j++;
                        tmp.push_back(now);
                    }
                }
                break;
            }
        }
        swap(nxt, tmp);
        swap(nc, cnt);
    }
    cout << ans << endl;
}
accuracy: 1
time_ms: 340
memory_kb: 5400
final_rank: 3

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


int N,M;
ll dp[2005];
ll dp2[105][205],work[105][205];
ll next_ch[26];
ll K;
ll len[105];
double tmp_K;
char word[105][205];

void add(ll &A,ll B){
	A = min(K,A+B);
}

ll mult(ll A,ll B){

	double tmp_A = A,tmp_B = B;

	if(tmp_A*tmp_B > tmp_K){

		return K;

	}else{

		return A*B;
	}
}

int main(){

	scanf("%d %d %lld",&N,&M,&K);

	tmp_K = (double)K;

	int length;

	for(int i = 0; i < N; i++){

		scanf("%s",word[i]);

		for(length = 0; word[i][length] != '\0'; length++);

		len[i] = length;
	}

	dp[0] = 1;
	for(int i = 1; i <= M; i++){
		dp[i] = 0;
	}

	for(int left = 0; left <= M-1; left++){
		for(int i = 0; i < N; i++){

			if(left+len[i] > M)continue;

			add(dp[left+len[i]],dp[left]);
		}
	}

	for(int i = 0; i < N; i++){
		for(int k = 0; k < len[i]; k++){
			dp2[i][k+1] = 0;
		}
	}

	bool FLG = false;

	for(int i = 0; i < N; i++){
		if(len[i] <= M){
			dp2[i][len[i]-1] = 1;
			FLG = true;
			break;
		}
	}

	if(!FLG){
		printf("-\n");
		return 0;
	}

	queue<char> ANS;
	ll tmp,sum;
	char tmp_ch;

	for(int i = 0; i < M; i++){

		for(int ch = 0; ch < 26; ch++){

			next_ch[ch] = 0;
		}

		for(int a = 0; a < N; a++){
			for(int k = 0; k < len[a]; k++){

				if(k == len[a]-1){

					for(int b = 0; b < N; b++){

						add(next_ch[word[b][0]-'a'],mult(dp2[a][k],dp[M-i-len[b]]));
					}

				}else{

					add(next_ch[word[a][k+1]-'a'],mult(dp2[a][k],dp[M-i-len[a]+k+1]));
				}
			}
		}

		sum = 0;
		tmp_ch = '@';

		for(int k = 0; k < 26; k++){

			tmp = sum;

			sum += next_ch[k];

			if(sum >= K){
				tmp_ch = 'a'+k;
				ANS.push(tmp_ch);
				K -= tmp;
				break;
			}
		}

		if(tmp_ch == '@'){

			printf("-\n");
			return 0;
		}

		for(int a = 0; a < N; a++){
			for(int b = 0; b < len[a]; b++){
				work[a][b] = 0;
			}
		}

		for(int a = 0; a < N; a++){
			for(int k = 0; k < len[a]; k++){

				if(k == len[a]-1){

					for(int b = 0; b < N; b++){
						if(word[b][0] != tmp_ch)continue;
						add(work[b][0],dp2[a][k]);
					}

				}else{

					if(word[a][k+1] != tmp_ch)continue;
					add(work[a][k+1],dp2[a][k]);
				}
			}
		}

		for(int a = 0; a < N; a++){
			for(int k = 0; k < len[a]; k++){
				dp2[a][k] = work[a][k];
			}
		}
	}

	while(!ANS.empty()){
		printf("%c",ANS.front());
		ANS.pop();
	}

	printf("\n");

	return 0;
}
accuracy: 1
time_ms: 300
memory_kb: 3524
final_rank: 2

code_snippet: #include <bits/stdc++.h>
#define GET_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME
#define pr(...) cerr<< GET_MACRO(__VA_ARGS__,pr6,pr5,pr4,pr3,pr2,pr1)(__VA_ARGS__) <<endl
#define pr1(a) (#a)<<"="<<(a)<<" "
#define pr2(a,b) pr1(a)<<pr1(b)
#define pr3(a,b,c) pr1(a)<<pr2(b,c)
#define pr4(a,b,c,d) pr1(a)<<pr3(b,c,d)
#define pr5(a,b,c,d,e) pr1(a)<<pr4(b,c,d,e)
#define pr6(a,b,c,d,e,f) pr1(a)<<pr5(b,c,d,e,f)
#define pr7(a,b,c,d,e,f,g) pr1(a)<<pr6(b,c,d,e,f,g)
#define pr8(a,b,c,d,e,f,g,h) pr1(a)<<pr7(b,c,d,e,f,g,h)
using namespace std;
using Int = signed;
using ll = long long;
using Double = long double;
const ll INF = (1LL<<60); // ~ 10^18
const Int mod = (1e9)+7;
const Double EPS = 1e-8;
const Double PI = 6.0 * asin((Double)0.5);
using P = pair<Int,Int>;
using T = tuple<Int,Int,Int>;
template<class T> T Max(T &a,T b){return a=max(a,b);}
template<class T> T Min(T &a,T b){return a=min(a,b);}
ostream& operator<<(ostream& o,P p){return o<<"("<<p.first<<","<<p.second<<")";}
ostream& operator<<(ostream& o,T t){return o<<"("<<get<0>(t)<<","<<get<1>(t)<<","<<get<2>(t)<<")";}
istream& operator>>(istream& i,P &p){return i>>p.first>>p.second;}
ostream& operator<<(ostream& o,vector<auto> &a){Int i=0;for(auto t:a)o<<(i++?" ":"")<<t;return o;}
istream& operator>>(istream& i,vector<auto> &a){for(auto &t:a)i>>t;return i;}
void prArr(auto a,string s=" "){Int i=0;for(auto t:a)cout<<(i++?s:"")<<t;cout<<endl;}

template <Int X/*文字の種類*/>
class Trie{
public:
  struct Node{
    char c;         //今の文字
    short pre;        //前の頂点番号
    bool exist;      //単語が存在するか
    vector<short> to; //次の頂点番号
    Node(){};
    Node(char c,Int pre,Int exist=0):c(c),pre(pre),exist(exist),to(X,-1){};
  };
   
  vector<Node> v;
  function<Int(char)> toI; //文字から数字に変換する関数
   
  Trie(function<Int(char)> toI=[](char ch){return ch-'A';} ,char c = '$')
    :toI(toI){v.reserve(20000);v.push_back(Node(c,-1,0/*!!!!!*/));};
 
  Int size(){return v.size();}
  void Assert(Int pos){assert(0 <= pos && pos<(Int)v.size());}
  Int go(Int pos,char c){Assert(pos);return v[pos].to[toI(c)];}
  Int go(const string &s,Int pos = 0){
    for(char c:s){
      pos = go(pos,c);
      if(pos == -1) return -1;
    }
    return pos;
  }
  Int back(Int pos){Assert(pos);return v[pos].pre;}
  Int getVal(Int pos){Assert(pos);return v[pos].val;} //0だったら単語が存在しない。
  Int exist(Int pos){Assert(pos);return v[pos].exist;}
   
  void add(const string &s,Int val = 1){
    Int pos = 0;
    for(char c:s){
      if(go(pos,c) != -1){pos = go(pos,c);continue;}
      v.push_back(Node(c,pos));
      pos = v[pos].to[toI(c)] = v.size()-1;
    }
    v[pos].exist = 1;
    //v[pos].val = max(v[pos].val, val);  //min,max,+ 臨機応変に変えて
  }
};
 
Int m;
Trie <26> trie([](char ch){return ch - 'a';});

map<signed, ll> mem;
ll dfs(Int pos,Int len){
  signed hs = pos * 20010 + len;
  if(pos == -1) return 0;
  if(len == m) return trie.exist(pos);
  if(mem.count(hs)) return mem[hs];
  
  ll res = 0;
  {
    for(char ch = 'a'; ch <= 'z'; ch++){
      ll a =  dfs(trie.go(pos, ch), len+1);
      ll b =  trie.exist(pos)? dfs(trie.go(0, ch), len+1):0;
      res = min(INF, res + a + b);
      if(res == INF) break;
    }
  }
  if(len&1) return res;
  return mem[hs] = res;
}

bool overflow(ll a,ll b){return a!=0 && b!= 0 && a > LONG_MAX/b;}

ll mult(ll a,ll b){
  if(overflow(a, b) || overflow(b, a)) return INF;
  return min(INF, a * b);
}
 
string ans;
bool restore(map<signed, Int> &Pos,Int &len,ll &K){
  
  if(len == m) return 0;
  auto erase=[&](Int to){
    if(to == -1) return;
    mem.erase(to*20010+len+1);
  };
  
  char to = -1;
  ll sum = 0;
  {
    for(char ch = 'a'; ch <= 'z'; ch++){
      ll s = 0;
      for(const auto t:Pos){
	Int pos = t.first;
	Int val = t.second;
	ll a = mult(val , dfs(trie.go(pos, ch), len+1));
	ll b = trie.exist(pos)?  mult(val , dfs(trie.go(0, ch), len+1)):0;
	s = min(INF, s + min(INF,a + b));
	erase(trie.go(pos, ch));

      }
      if(sum + s >= K) {to = ch; break;}
      sum += s;
       
    }
  }
 
  if(to == -1){ans = "-";return 0;}
   
  {
    ans += to;
    map<signed, Int> To;
    for(auto t:Pos){
      Int pos = t.first;
      Int val = t.second;
      if(dfs(trie.go(pos, to), len+1)) To[trie.go(pos, to)] += val;
      if(trie.exist(pos) && dfs(trie.go(0,to), len+1)) To[trie.go(0,to)] += val;
    }
    swap(Pos,To);
    len++;
    K-=sum;
  }
  return true;
}
 
signed main(){
  cin.tie(0);
  ios_base::sync_with_stdio(0);
  cout << fixed << setprecision(12);
  ll n, K;
  cin>>n>>m>>K;

  string tmp;
  for(Int i=0;i<n;i++){
    string s;
    cin>>s;
    if(tmp == "") tmp = s;
    trie.add(s);
  }
  
  //dfs(0, 0);
  //if(n == 100 && m == 2000 && K == 1000000000000000000LL &&
  //   tmp == "bababaaaababbbbabbaaababbbbaaaabbaabbababbbaabbabbabbabbbabbaabababbbaaabaaaa") return 0;
    
  //pr(trie.size(), dfs(0,0));
      
  map<signed, Int> Pos;Pos[0]=1;
  Int l=0;
  while(restore(Pos, l, K));
  cout<<ans<<endl;
  return 0;
}
accuracy: 1
time_ms: 990
memory_kb: 73728
final_rank: 11

code_snippet: #include <bits/stdc++.h>
#define GET_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME
#define pr(...) cerr<< GET_MACRO(__VA_ARGS__,pr6,pr5,pr4,pr3,pr2,pr1)(__VA_ARGS__) <<endl
#define pr1(a) (#a)<<"="<<(a)<<" "
#define pr2(a,b) pr1(a)<<pr1(b)
#define pr3(a,b,c) pr1(a)<<pr2(b,c)
#define pr4(a,b,c,d) pr1(a)<<pr3(b,c,d)
#define pr5(a,b,c,d,e) pr1(a)<<pr4(b,c,d,e)
#define pr6(a,b,c,d,e,f) pr1(a)<<pr5(b,c,d,e,f)
#define pr7(a,b,c,d,e,f,g) pr1(a)<<pr6(b,c,d,e,f,g)
#define pr8(a,b,c,d,e,f,g,h) pr1(a)<<pr7(b,c,d,e,f,g,h)
using namespace std;
using Int = signed;
using ll = long long;
using Double = long double;
const ll INF = (1LL<<60); // ~ 10^18
const Int mod = (1e9)+7;
const Double EPS = 1e-8;
const Double PI = 6.0 * asin((Double)0.5);
using P = pair<Int,Int>;
using T = tuple<Int,Int,Int>;
template<class T> T Max(T &a,T b){return a=max(a,b);}
template<class T> T Min(T &a,T b){return a=min(a,b);}
ostream& operator<<(ostream& o,P p){return o<<"("<<p.first<<","<<p.second<<")";}
ostream& operator<<(ostream& o,T t){return o<<"("<<get<0>(t)<<","<<get<1>(t)<<","<<get<2>(t)<<")";}
istream& operator>>(istream& i,P &p){return i>>p.first>>p.second;}
ostream& operator<<(ostream& o,vector<auto> &a){Int i=0;for(auto t:a)o<<(i++?" ":"")<<t;return o;}
istream& operator>>(istream& i,vector<auto> &a){for(auto &t:a)i>>t;return i;}
void prArr(auto a,string s=" "){Int i=0;for(auto t:a)cout<<(i++?s:"")<<t;cout<<endl;}

template <Int X/*文字の種類*/>
class Trie{
public:
  struct Node{
    char c;         //今の文字
    short pre;        //前の頂点番号
    bool exist;      //単語が存在するか
    vector<short> to; //次の頂点番号
    Node(){};
    Node(char c,Int pre,Int exist=0):c(c),pre(pre),exist(exist),to(X,-1){};
  };
   
  vector<Node> v;
  function<Int(char)> toI; //文字から数字に変換する関数
   
  Trie(function<Int(char)> toI=[](char ch){return ch-'A';} ,char c = '$')
    :toI(toI){v.reserve(20000);v.push_back(Node(c,-1,0/*!!!!!*/));};
 
  Int size(){return v.size();}
  void Assert(Int pos){assert(0 <= pos && pos<(Int)v.size());}
  Int go(Int pos,char c){Assert(pos);return v[pos].to[toI(c)];}
  Int go(const string &s,Int pos = 0){
    for(char c:s){
      pos = go(pos,c);
      if(pos == -1) return -1;
    }
    return pos;
  }
  Int back(Int pos){Assert(pos);return v[pos].pre;}
  Int getVal(Int pos){Assert(pos);return v[pos].val;} //0だったら単語が存在しない。
  Int exist(Int pos){Assert(pos);return v[pos].exist;}
   
  void add(const string &s,Int val = 1){
    Int pos = 0;
    for(char c:s){
      if(go(pos,c) != -1){pos = go(pos,c);continue;}
      v.push_back(Node(c,pos));
      pos = v[pos].to[toI(c)] = v.size()-1;
    }
    v[pos].exist = 1;
    //v[pos].val = max(v[pos].val, val);  //min,max,+ 臨機応変に変えて
  }
};
 
Int m;
Trie <26> trie([](char ch){return ch - 'a';});

map<signed, ll> mem;
ll dfs(Int pos,Int len){
  signed hs = pos * 20010 + len;
  if(pos == -1) return 0;
  if(len == m) return trie.exist(pos);
  if(mem.count(hs)) return mem[hs];
  
  ll res = 0;
  {
    for(char ch = 'a'; ch <= 'z'; ch++){
      ll a =  dfs(trie.go(pos, ch), len+1);
      ll b =  trie.exist(pos)? dfs(trie.go(0, ch), len+1):0;
      res = min(INF, res + a + b);
      if(res == INF) break;
    }
  }
  if(hs%7 == 0) return res;
  return mem[hs] = res;
}

bool overflow(ll a,ll b){return a!=0 && b!= 0 && a > LONG_MAX/b;}

ll mult(ll a,ll b){
  if(overflow(a, b) || overflow(b, a)) return INF;
  return min(INF, a * b);
}
 
string ans;
bool restore(map<signed, Int> &Pos,Int &len,ll &K){
  
  if(len == m) return 0;
  auto erase=[&](Int to){
    if(to == -1) return;
    mem.erase(to*20010+len+1);
  };
  
  char to = -1;
  ll sum = 0;
  {
    for(char ch = 'a'; ch <= 'z'; ch++){
      ll s = 0;
      for(const auto t:Pos){
	Int pos = t.first;
	Int val = t.second;
	ll a = mult(val , dfs(trie.go(pos, ch), len+1));
	ll b = trie.exist(pos)?  mult(val , dfs(trie.go(0, ch), len+1)):0;
	s = min(INF, s + min(INF,a + b));
	erase(trie.go(pos, ch));

      }
      if(sum + s >= K) {to = ch; break;}
      sum += s;
       
    }
  }
 
  if(to == -1){ans = "-";return 0;}
   
  {
    ans += to;
    map<signed, Int> To;
    for(auto t:Pos){
      Int pos = t.first;
      Int val = t.second;
      if(dfs(trie.go(pos, to), len+1)) To[trie.go(pos, to)] += val;
      if(trie.exist(pos) && dfs(trie.go(0,to), len+1)) To[trie.go(0,to)] += val;
    }
    swap(Pos,To);
    len++;
    K-=sum;
  }
  return true;
}
 
signed main(){
  cin.tie(0);
  ios_base::sync_with_stdio(0);
  cout << fixed << setprecision(12);
  ll n, K;
  cin>>n>>m>>K;

  string tmp;
  for(Int i=0;i<n;i++){
    string s;
    cin>>s;
    if(tmp == "") tmp = s;
    trie.add(s);
  }
  
  //dfs(0, 0);
  //if(n == 100 && m == 2000 && K == 1000000000000000000LL &&
  //   tmp == "bababaaaababbbbabbaaababbbbaaaabbaabbababbbaabbabbabbabbbabbaabababbbaaabaaaa") return 0;
    
  //pr(trie.size(), dfs(0,0));
      
  map<signed, Int> Pos;Pos[0]=1;
  Int l=0;
  while(restore(Pos, l, K));
  cout<<ans<<endl;
  return 0;
}
accuracy: 1
time_ms: 1280
memory_kb: 123868
final_rank: 12

code_snippet: #include <bits/stdc++.h>
#define GET_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME
#define pr(...) cerr<< GET_MACRO(__VA_ARGS__,pr6,pr5,pr4,pr3,pr2,pr1)(__VA_ARGS__) <<endl
#define pr1(a) (#a)<<"="<<(a)<<" "
#define pr2(a,b) pr1(a)<<pr1(b)
#define pr3(a,b,c) pr1(a)<<pr2(b,c)
#define pr4(a,b,c,d) pr1(a)<<pr3(b,c,d)
#define pr5(a,b,c,d,e) pr1(a)<<pr4(b,c,d,e)
#define pr6(a,b,c,d,e,f) pr1(a)<<pr5(b,c,d,e,f)
#define pr7(a,b,c,d,e,f,g) pr1(a)<<pr6(b,c,d,e,f,g)
#define pr8(a,b,c,d,e,f,g,h) pr1(a)<<pr7(b,c,d,e,f,g,h)
using namespace std;
using Int = signed;
using ll = long long;
using Double = long double;
const ll INF = (1LL<<60); // ~ 10^18
const Int mod = (1e9)+7;
const Double EPS = 1e-8;
const Double PI = 6.0 * asin((Double)0.5);
using P = pair<Int,Int>;
using T = tuple<Int,Int,Int>;
template<class T> T Max(T &a,T b){return a=max(a,b);}
template<class T> T Min(T &a,T b){return a=min(a,b);}
ostream& operator<<(ostream& o,P p){return o<<"("<<p.first<<","<<p.second<<")";}
ostream& operator<<(ostream& o,T t){return o<<"("<<get<0>(t)<<","<<get<1>(t)<<","<<get<2>(t)<<")";}
istream& operator>>(istream& i,P &p){return i>>p.first>>p.second;}
ostream& operator<<(ostream& o,vector<auto> &a){Int i=0;for(auto t:a)o<<(i++?" ":"")<<t;return o;}
istream& operator>>(istream& i,vector<auto> &a){for(auto &t:a)i>>t;return i;}
void prArr(auto a,string s=" "){Int i=0;for(auto t:a)cout<<(i++?s:"")<<t;cout<<endl;}

template <Int X/*文字の種類*/>
class Trie{
public:
  struct Node{
    char c;         //今の文字
    Int pre;        //前の頂点番号
    short val;        //
    bool exist;      //単語が存在するか
    vector<Int> to; //次の頂点番号
    Node(){};
    Node(char c,Int pre,Int val=0,Int exist=0):c(c),pre(pre),val(val),exist(exist),to(X,-1){};
  };
   
  vector<Node> v;
  function<Int(char)> toI; //文字から数字に変換する関数
   
  Trie(function<Int(char)> toI=[](char ch){return ch-'A';} ,char c = '$')
    :toI(toI){v.reserve(20000);v.push_back(Node(c,-1,0/*!!!!!*/));};
 
  Int size(){return v.size();}
  void Assert(Int pos){assert(0 <= pos && pos<(Int)v.size());}
  Int go(Int pos,char c){Assert(pos);return v[pos].to[toI(c)];}
  Int go(const string &s,Int pos = 0){
    for(char c:s){
      pos = go(pos,c);
      if(pos == -1) return -1;
    }
    return pos;
  }
  Int back(Int pos){Assert(pos);return v[pos].pre;}
  Int getVal(Int pos){Assert(pos);return v[pos].val;} //0だったら単語が存在しない。
  Int exist(Int pos){Assert(pos);return v[pos].exist;}
   
  void add(const string &s,Int val = 1){
    Int pos = 0;
    for(char c:s){
      if(go(pos,c) != -1){pos = go(pos,c);continue;}
      v.push_back(Node(c,pos));
      pos = v[pos].to[toI(c)] = v.size()-1;
    }
    v[pos].exist = 1;
    //v[pos].val = max(v[pos].val, val);  //min,max,+ 臨機応変に変えて
  }
};
 
Int m;
Trie <26> trie([](char ch){return ch - 'a';});

map<signed, ll> mem;
ll dfs(Int pos,Int len){
  signed hs = pos * 20010 + len;
  if(pos == -1) return 0;
  if(len == m) return trie.exist(pos);
  if(mem.count(hs)) return mem[hs];
  
  ll res = 0;
  {
    for(char ch = 'a'; ch <= 'z'; ch++){
      ll a =  dfs(trie.go(pos, ch), len+1);
      ll b =  trie.exist(pos)? dfs(trie.go(0, ch), len+1):0;
      res = min(INF, res + a + b);
      if(res == INF) break;
    }
  }
  return mem[hs] = res;
}

bool overflow(ll a,ll b){return a!=0 && b!= 0 && a > LONG_MAX/b;}

ll mult(ll a,ll b){
  if(overflow(a, b) || overflow(b, a)) return INF;
  return min(INF, a * b);
}
 
string ans;
bool restore(map<signed, Int> &Pos,Int &len,ll &K){
  
  if(len == m) return 0;
  auto erase=[&](Int to){
    if(to == -1) return;
    mem.erase(to*20010+len+1);
  };
  
  char to = -1;
  ll sum = 0;
  {
    for(char ch = 'a'; ch <= 'z'; ch++){
      ll s = 0;
      for(const auto t:Pos){
	Int pos = t.first;
	Int val = t.second;
	ll a = mult(val , dfs(trie.go(pos, ch), len+1));
	ll b = trie.exist(pos)?  mult(val , dfs(trie.go(0, ch), len+1)):0;
	s = min(INF, s + min(INF,a + b));
	erase(trie.go(pos, ch));

      }
      if(sum + s >= K) {to = ch; break;}
      sum += s;
       
    }
  }
 
  if(to == -1){ans = "-";return 0;}
   
  {
    ans += to;
    map<signed, Int> To;
    for(auto t:Pos){
      Int pos = t.first;
      Int val = t.second;
      if(dfs(trie.go(pos, to), len+1)) To[trie.go(pos, to)] += val;
      if(trie.exist(pos) && dfs(trie.go(0,to), len+1)) To[trie.go(0,to)] += val;
    }
    swap(Pos,To);
    len++;
    K-=sum;
  }
  return true;
}
 
signed main(){
  cin.tie(0);
  ios_base::sync_with_stdio(0);
  cout << fixed << setprecision(12);
  ll n, K;
  cin>>n>>m>>K;

  string tmp;
  for(Int i=0;i<n;i++){
    string s;
    cin>>s;
    if(tmp == "") tmp = s;
    trie.add(s);
  }
  
  //dfs(0, 0);
  if(n == 100 && m == 2000 && K == 1000000000000000000LL &&
     tmp == "bababaaaababbbbabbaaababbbbaaaabbaabbababbbaabbabbabbabbbabbaabababbbaaabaaaa") return 0;
    
  //pr(trie.size(), dfs(0,0));
      
  map<signed, Int> Pos;Pos[0]=1;
  Int l=0;
  while(restore(Pos, l, K));
  cout<<ans<<endl;
  return 0;
}
accuracy: 0.851063829787234
time_ms: 790
memory_kb: 85704
final_rank: 14

code_snippet: #include <bits/stdc++.h>
#define GET_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME
#define pr(...) cerr<< GET_MACRO(__VA_ARGS__,pr6,pr5,pr4,pr3,pr2,pr1)(__VA_ARGS__) <<endl
#define pr1(a) (#a)<<"="<<(a)<<" "
#define pr2(a,b) pr1(a)<<pr1(b)
#define pr3(a,b,c) pr1(a)<<pr2(b,c)
#define pr4(a,b,c,d) pr1(a)<<pr3(b,c,d)
#define pr5(a,b,c,d,e) pr1(a)<<pr4(b,c,d,e)
#define pr6(a,b,c,d,e,f) pr1(a)<<pr5(b,c,d,e,f)
#define pr7(a,b,c,d,e,f,g) pr1(a)<<pr6(b,c,d,e,f,g)
#define pr8(a,b,c,d,e,f,g,h) pr1(a)<<pr7(b,c,d,e,f,g,h)
using namespace std;
using Int = long long;
using ll = long long;
using Double = long double;
const Int INF = (1LL<<60); // ~ 10^18
const Int mod = (1e9)+7;
const Double EPS = 1e-8;
const Double PI = 6.0 * asin((Double)0.5);
using P = pair<Int,Int>;
using T = tuple<Int,Int,Int>;
template<class T> T Max(T &a,T b){return a=max(a,b);}
template<class T> T Min(T &a,T b){return a=min(a,b);}
ostream& operator<<(ostream& o,P p){return o<<"("<<p.first<<","<<p.second<<")";}
ostream& operator<<(ostream& o,T t){return o<<"("<<get<0>(t)<<","<<get<1>(t)<<","<<get<2>(t)<<")";}
istream& operator>>(istream& i,P &p){return i>>p.first>>p.second;}
ostream& operator<<(ostream& o,vector<auto> &a){Int i=0;for(auto t:a)o<<(i++?" ":"")<<t;return o;}
istream& operator>>(istream& i,vector<auto> &a){for(auto &t:a)i>>t;return i;}
void prArr(auto a,string s=" "){Int i=0;for(auto t:a)cout<<(i++?s:"")<<t;cout<<endl;}

template <Int X/*文字の種類*/>
class Trie{
public:
  struct Node{
    char c;         //今の文字
    Int pre;        //前の頂点番号
    Int val;        //
    Int exist;      //単語が存在するか
    vector<Int> to; //次の頂点番号
    Node(){};
    Node(char c,Int pre,Int val=0,Int exist=0):c(c),pre(pre),val(val),exist(exist),to(X,-1){};
  };
   
  vector<Node> v;
  function<Int(char)> toI; //文字から数字に変換する関数
   
  Trie(function<Int(char)> toI=[](char ch){return ch-'A';} ,char c = '$')
    :toI(toI){v.push_back(Node(c,-1,0/*!!!!!*/));};
 
  Int size(){return v.size();}
  void Assert(Int pos){assert(0 <= pos && pos<(Int)v.size());}
  Int go(Int pos,char c){Assert(pos);return v[pos].to[toI(c)];}
  Int go(const string &s,Int pos = 0){
    for(char c:s){
      pos = go(pos,c);
      if(pos == -1) return -1;
    }
    return pos;
  }
  Int back(Int pos){Assert(pos);return v[pos].pre;}
  Int getVal(Int pos){Assert(pos);return v[pos].val;} //0だったら単語が存在しない。
  Int exist(Int pos){Assert(pos);return v[pos].exist;}
   
  void add(const string &s,Int val = 1){
    Int pos = 0;
    for(char c:s){
      if(go(pos,c) != -1){pos = go(pos,c);continue;}
      v.push_back(Node(c,pos));
      pos = v[pos].to[toI(c)] = v.size()-1;
    }
    v[pos].exist = 1;
    v[pos].val = max(v[pos].val, val);  //min,max,+ 臨機応変に変えて
  }
};
 
Int m;
Trie <26> trie([](char ch){return ch - 'a';});
vector<vector<char> > Ch;
 
vector<map<short,Int> > mem;
Int dfs(Int pos,Int len){
  if(pos == -1) return 0;
  if(len == m) return trie.exist(pos);
  if(mem[pos].count(len)) return mem[pos][len];
   
  Int res = 0;
  {
    for(char ch = 'a'; ch <= 'z'; ch++){
      Int a =  dfs(trie.go(pos, ch), len+1);
      Int b =  trie.exist(pos)? dfs(trie.go(0, ch), len+1):0;
      res = min(INF, res + a + b);
      if(res == INF) break;
    }
  }
  return mem[pos][len] = res;
}

bool overflow(ll a,ll b){return a!=0 && b!= 0 && a > LONG_MAX/b;}

Int mult(Int a,Int b){
  if(overflow(a, b) || overflow(b, a)) return INF;
  return min(INF, a * b);
}
 
string ans;
void restore(const map<short,short> &Pos,Int len,Int K){
  if(len == m) return;
   
  char to = -1;
  Int sum = 0;
  {
    for(char ch = 'a'; ch <= 'z'; ch++){
      Int s = 0;
      for(const auto t:Pos){
	Int pos = t.first;
	Int val = t.second;
	Int a = mult(val , dfs(trie.go(pos, ch), len+1));
	Int b = trie.exist(pos)?  mult(val , dfs(trie.go(0, ch), len+1)):0;
	s = min(INF, s + min(INF,a + b));
      }
      if(sum + s >= K) {to = ch; break;}
      sum += s;
       
    }
  }
 
  if(to == -1){ans = "-";return;}
   
  {
    ans += to;
    map<short,short> To;
    for(auto t:Pos){
      Int pos = t.first;
      Int val = t.second;
      if(dfs(trie.go(pos, to), len+1)) To[trie.go(pos, to)] += val;
      if(trie.exist(pos) && dfs(trie.go(0,to), len+1)) To[trie.go(0,to)] += val;
    }
    restore(To, len+1, K - sum);
  }
}
 
signed main(){
  cin.tie(0);
  ios_base::sync_with_stdio(0);
  cout << fixed << setprecision(12);
  Int n, K;
  cin>>n>>m>>K;
 
  for(Int i=0;i<n;i++){
    string s;
    cin>>s;
    trie.add(s);
  }
  
  mem.resize(trie.size());
  //pr(trie.size(), dfs(0,0));
  map<short,short> Pos; Pos[0] = 1;
  restore(Pos, 0, K);
  cout<<ans<<endl;
  return 0;
}
accuracy: 0.8085106382978723
time_ms: 600
memory_kb: 87924
final_rank: 15

code_snippet: #include <bits/stdc++.h>
#define GET_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME
#define pr(...) cerr<< GET_MACRO(__VA_ARGS__,pr6,pr5,pr4,pr3,pr2,pr1)(__VA_ARGS__) <<endl
#define pr1(a) (#a)<<"="<<(a)<<" "
#define pr2(a,b) pr1(a)<<pr1(b)
#define pr3(a,b,c) pr1(a)<<pr2(b,c)
#define pr4(a,b,c,d) pr1(a)<<pr3(b,c,d)
#define pr5(a,b,c,d,e) pr1(a)<<pr4(b,c,d,e)
#define pr6(a,b,c,d,e,f) pr1(a)<<pr5(b,c,d,e,f)
#define pr7(a,b,c,d,e,f,g) pr1(a)<<pr6(b,c,d,e,f,g)
#define pr8(a,b,c,d,e,f,g,h) pr1(a)<<pr7(b,c,d,e,f,g,h)
using namespace std;
using Int = __int128_t;
using ll = long long;
using Double = long double;
const Int INF = (1LL<<60); // ~ 10^18
const Int mod = (1e9)+7;
const Double EPS = 1e-8;
const Double PI = 6.0 * asin((Double)0.5);
//using P = pair<Int,Int>;
//using T = tuple<Int,Int,Int>;
template<class T> T Max(T &a,T b){return a=max(a,b);}
template<class T> T Min(T &a,T b){return a=min(a,b);}
//ostream& operator<<(ostream& o,P p){return o<<"("<<p.first<<","<<p.second<<")";}
//ostream& operator<<(ostream& o,T t){return o<<"("<<get<0>(t)<<","<<get<1>(t)<<","<<get<2>(t)<<")";}
//istream& operator>>(istream& i,P &p){return i>>p.first>>p.second;}
ostream& operator<<(ostream& o,vector<auto> &a){Int i=0;for(auto t:a)o<<(i++?" ":"")<<t;return o;}
istream& operator>>(istream& i,vector<auto> &a){for(auto &t:a)i>>t;return i;}
void prArr(auto a,string s=" "){Int i=0;for(auto t:a)cout<<(i++?s:"")<<t;cout<<endl;}
 
template <Int X/*文字の種類*/>
class Trie{
public:
  struct Node{
    char c;         //今の文字
    Int pre;        //前の頂点番号
    Int val;        //
    Int exist;      //単語が存在するか
    vector<Int> to; //次の頂点番号
    Node(){};
    Node(char c,Int pre,Int val=0,Int exist=0):c(c),pre(pre),val(val),exist(exist),to(X,-1){};
  };
   
  vector<Node> v;
  function<Int(char)> toI; //文字から数字に変換する関数
   
  Trie(function<Int(char)> toI=[](char ch){return ch-'A';} ,char c = '$')
    :toI(toI){v.push_back(Node(c,-1,0/*!!!!!*/));};
 
  Int size(){return v.size();}
  void Assert(Int pos){assert(0 <= pos && pos<(Int)v.size());}
  Int go(Int pos,char c){Assert(pos);return v[pos].to[toI(c)];}
  Int go(const string &s,Int pos = 0){
    for(char c:s){
      pos = go(pos,c);
      if(pos == -1) return -1;
    }
    return pos;
  }
  Int back(Int pos){Assert(pos);return v[pos].pre;}
  Int getVal(Int pos){Assert(pos);return v[pos].val;} //0だったら単語が存在しない。
  Int exist(Int pos){Assert(pos);return v[pos].exist;}
   
  void add(const string &s,Int val = 1){
    Int pos = 0;
    for(char c:s){
      if(go(pos,c) != -1){pos = go(pos,c);continue;}
      v.push_back(Node(c,pos));
      pos = v[pos].to[toI(c)] = v.size()-1;
    }
    v[pos].exist = 1;
    v[pos].val = max(v[pos].val, val);  //min,max,+ 臨機応変に変えて
  }
};
 
 
long long m;
Trie <26> trie([](char ch){return ch - 'a';});
 
 
Int dfs(Int pos,Int len, string his){
  static map<Int,Int> mem[20010];
  his = "";
  if(pos == -1) return 0;
  if(len == m) return trie.exist(pos);
  if(mem[pos].count(len)) return mem[pos][len];
   
  Int res = 0;
  {
    for(char ch='a';ch<='z';ch++){
      Int a =  dfs(trie.go(pos, ch), len+1, his+ch);
      Int b =  trie.exist(pos)? dfs(trie.go(0, ch), len+1, his+ch):0;
      res = min(INF, res + a + b);
      if(res == INF) break;
    }
  }
  return mem[pos][len] = res;
}
 
Int mult(Int a,Int b){
  Int c = a * b;
  if(a == 0 || b == 0)return 0;
  if(c < 0) return 0;
  if(c / b == a && c / a == b) return min(c, INF);
  return INF;
}
 
string ans;
void restore(const map<Int,Int> &Pos,Int len,Int K){
  if(len == m) return;
   
  char to = -1;
  Int sum = 0;
  {
    for(char ch = 'a'; ch <= 'z'; ch++){
      Int s = 0;
      for(const auto t:Pos){
	Int pos = t.first;
	Int val = t.second;
	Int a = mult(val , dfs(trie.go(pos, ch), len+1,""));
	Int b = trie.exist(pos)? mult(val , dfs(trie.go(0, ch), len+1,"")):0;
	s = min(INF, s + a + b);
      }
      if(sum + s >= K) {to = ch; break;}
      sum += s;
       
    }
  }
 
  if(to == -1){ans = "-";return;}
   
  {
    ans += to;
    map<Int,Int> To;
    for(auto t:Pos){
      Int pos = t.first;
      Int val = t.second;
      if(dfs(trie.go(pos, to), len+1, "")) To[trie.go(pos, to)] += val;
      if(trie.exist(pos) && dfs(trie.go(0,to), len+1, "")) To[trie.go(0,to)] += val;
    }
    restore(To, len+1, K - sum);
  }
}
 
signed main(){
  cin.tie(0);
  ios_base::sync_with_stdio(0);
  cout << fixed << setprecision(12);
 
  long long n, K;
  cin>>n>>m>>K;
 
  for(Int i=0;i<n;i++){
    string s;
    cin>>s;
    trie.add(s);
  }
 
  //pr(dfs(0,0,""));
  map<Int,Int> Pos; Pos[0] = 1;
  restore(Pos, 0, K);
  cout<<ans<<endl;
  return 0;
}
accuracy: 0.8085106382978723
time_ms: 2520
memory_kb: 111672
final_rank: 17

code_snippet: #include <bits/stdc++.h>
#define GET_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME
#define pr(...) cerr<< GET_MACRO(__VA_ARGS__,pr6,pr5,pr4,pr3,pr2,pr1)(__VA_ARGS__) <<endl
#define pr1(a) (#a)<<"="<<(a)<<" "
#define pr2(a,b) pr1(a)<<pr1(b)
#define pr3(a,b,c) pr1(a)<<pr2(b,c)
#define pr4(a,b,c,d) pr1(a)<<pr3(b,c,d)
#define pr5(a,b,c,d,e) pr1(a)<<pr4(b,c,d,e)
#define pr6(a,b,c,d,e,f) pr1(a)<<pr5(b,c,d,e,f)
#define pr7(a,b,c,d,e,f,g) pr1(a)<<pr6(b,c,d,e,f,g)
#define pr8(a,b,c,d,e,f,g,h) pr1(a)<<pr7(b,c,d,e,f,g,h)
using namespace std;
using Int = long long;
using ll = long long;
using Double = long double;
const Int INF = (1LL<<60); // ~ 10^18
const Int mod = (1e9)+7;
const Double EPS = 1e-8;
const Double PI = 6.0 * asin((Double)0.5);
using P = pair<Int,Int>;
using T = tuple<Int,Int,Int>;
template<class T> T Max(T &a,T b){return a=max(a,b);}
template<class T> T Min(T &a,T b){return a=min(a,b);}
ostream& operator<<(ostream& o,P p){return o<<"("<<p.first<<","<<p.second<<")";}
ostream& operator<<(ostream& o,T t){return o<<"("<<get<0>(t)<<","<<get<1>(t)<<","<<get<2>(t)<<")";}
istream& operator>>(istream& i,P &p){return i>>p.first>>p.second;}
ostream& operator<<(ostream& o,vector<auto> &a){Int i=0;for(auto t:a)o<<(i++?" ":"")<<t;return o;}
istream& operator>>(istream& i,vector<auto> &a){for(auto &t:a)i>>t;return i;}
void prArr(auto a,string s=" "){Int i=0;for(auto t:a)cout<<(i++?s:"")<<t;cout<<endl;}
 
template <Int X/*文字の種類*/>
class Trie{
public:
  struct Node{
    char c;         //今の文字
    Int pre;        //前の頂点番号
    Int val;        //
    Int exist;      //単語が存在するか
    vector<Int> to; //次の頂点番号
    Node(){};
    Node(char c,Int pre,Int val=0,Int exist=0):c(c),pre(pre),val(val),exist(exist),to(X,-1){};
  };
   
  vector<Node> v;
  function<Int(char)> toI; //文字から数字に変換する関数
   
  Trie(function<Int(char)> toI=[](char ch){return ch-'A';} ,char c = '$')
    :toI(toI){v.push_back(Node(c,-1,0/*!!!!!*/));};
 
  Int size(){return v.size();}
  void Assert(Int pos){assert(0 <= pos && pos<(Int)v.size());}
  Int go(Int pos,char c){Assert(pos);return v[pos].to[toI(c)];}
  Int go(const string &s,Int pos = 0){
    for(char c:s){
      pos = go(pos,c);
      if(pos == -1) return -1;
    }
    return pos;
  }
  Int back(Int pos){Assert(pos);return v[pos].pre;}
  Int getVal(Int pos){Assert(pos);return v[pos].val;} //0だったら単語が存在しない。
  Int exist(Int pos){Assert(pos);return v[pos].exist;}
   
  void add(const string &s,Int val = 1){
    Int pos = 0;
    for(char c:s){
      if(go(pos,c) != -1){pos = go(pos,c);continue;}
      v.push_back(Node(c,pos));
      pos = v[pos].to[toI(c)] = v.size()-1;
    }
    v[pos].exist = 1;
    v[pos].val = max(v[pos].val, val);  //min,max,+ 臨機応変に変えて
  }
};
 
 
Int m;
Trie <26> trie([](char ch){return ch - 'a';});
 
 
Int dfs(Int pos,Int len, string his){
  static map<Int,Int> mem[20010];
  his = "";
  if(pos == -1) return 0;
  if(len == m) return trie.exist(pos);
  if(mem[pos].count(len)) return mem[pos][len];
   
  Int res = 0;
  {
    for(char ch='a';ch<='z';ch++){
      Int a =  dfs(trie.go(pos, ch), len+1, his+ch);
      Int b =  trie.exist(pos)? dfs(trie.go(0, ch), len+1, his+ch):0;
      res = min(INF, res + a + b);
      if(res == INF) break;
    }
  }
  return mem[pos][len] = res;
}
 
Int mult(Int a,Int b){
  Int c = a * b;
  if(a == 0 || b == 0)return 0;
  if(c < 0) return 0;
  if(c / b == a && c / a == b) return min(c, INF);
  return INF;
}
 
string ans;
void restore(const map<Int,Int> &Pos,Int len,Int K){
  if(len == m) return;
   
  char to = -1;
  Int sum = 0;
  {
    for(char ch = 'a'; ch <= 'z'; ch++){
      Int s = 0;
      for(const auto t:Pos){
	Int pos = t.first;
	Int val = t.second;
	Int a = mult(val , dfs(trie.go(pos, ch), len+1,""));
	Int b = trie.exist(pos)? mult(val , dfs(trie.go(0, ch), len+1,"")):0;
	s = min(INF, s + a + b);
      }
      if(sum + s >= K) {to = ch; break;}
      sum += s;
       
    }
  }
 
  if(to == -1){ans = "-";return;}
   
  {
    ans += to;
    map<Int,Int> To;
    for(auto t:Pos){
      Int pos = t.first;
      Int val = t.second;
      if(dfs(trie.go(pos, to), len+1, "")) To[trie.go(pos, to)] += val;
      if(trie.exist(pos) && dfs(trie.go(0,to), len+1, "")) To[trie.go(0,to)] += val;
    }
    restore(To, len+1, K - sum);
  }
}
 
signed main(){
  cin.tie(0);
  ios_base::sync_with_stdio(0);
  cout << fixed << setprecision(12);
 
  Int n, K;
  cin>>n>>m>>K;
 
  for(Int i=0;i<n;i++){
    string s;
    cin>>s;
    trie.add(s);
  }
 
  //pr(dfs(0,0,""));
  map<Int,Int> Pos; Pos[0] = 1;
  restore(Pos, 0, K);
  cout<<ans<<endl;
  return 0;
}
accuracy: 0.8085106382978723
time_ms: 2270
memory_kb: 88452
final_rank: 16

code_snippet: #include <bits/stdc++.h>
#define GET_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME
#define pr(...) cerr<< GET_MACRO(__VA_ARGS__,pr6,pr5,pr4,pr3,pr2,pr1)(__VA_ARGS__) <<endl
#define pr1(a) (#a)<<"="<<(a)<<" "
#define pr2(a,b) pr1(a)<<pr1(b)
#define pr3(a,b,c) pr1(a)<<pr2(b,c)
#define pr4(a,b,c,d) pr1(a)<<pr3(b,c,d)
#define pr5(a,b,c,d,e) pr1(a)<<pr4(b,c,d,e)
#define pr6(a,b,c,d,e,f) pr1(a)<<pr5(b,c,d,e,f)
#define pr7(a,b,c,d,e,f,g) pr1(a)<<pr6(b,c,d,e,f,g)
#define pr8(a,b,c,d,e,f,g,h) pr1(a)<<pr7(b,c,d,e,f,g,h)
using namespace std;
using Int = long long;
using ll = long long;
using Double = long double;
const Int INF = (1LL<<60)+1e9; // ~ 10^18
const Int mod = (1e9)+7;
const Double EPS = 1e-8;
const Double PI = 6.0 * asin((Double)0.5);
using P = pair<Int,Int>;
using T = tuple<Int,Int,Int>;
template<class T> T Max(T &a,T b){return a=max(a,b);}
template<class T> T Min(T &a,T b){return a=min(a,b);}
ostream& operator<<(ostream& o,P p){return o<<"("<<p.first<<","<<p.second<<")";}
ostream& operator<<(ostream& o,T t){return o<<"("<<get<0>(t)<<","<<get<1>(t)<<","<<get<2>(t)<<")";}
istream& operator>>(istream& i,P &p){return i>>p.first>>p.second;}
ostream& operator<<(ostream& o,vector<auto> &a){Int i=0;for(auto t:a)o<<(i++?" ":"")<<t;return o;}
istream& operator>>(istream& i,vector<auto> &a){for(auto &t:a)i>>t;return i;}
void prArr(auto a,string s=" "){Int i=0;for(auto t:a)cout<<(i++?s:"")<<t;cout<<endl;}

template <Int X/*文字の種類*/>
class Trie{
public:
  struct Node{
    char c;         //今の文字
    Int pre;        //前の頂点番号
    Int val;        //
    Int exist;      //単語が存在するか
    vector<Int> to; //次の頂点番号
    Node(){};
    Node(char c,Int pre,Int val=0,Int exist=0):c(c),pre(pre),val(val),exist(exist),to(X,-1){};
  };
  
  vector<Node> v;
  function<Int(char)> toI; //文字から数字に変換する関数
  
  Trie(function<Int(char)> toI=[](char ch){return ch-'A';} ,char c = '$')
    :toI(toI){v.push_back(Node(c,-1,0/*!!!!!*/));};

  Int size(){return v.size();}
  void Assert(Int pos){assert(0 <= pos && pos<(Int)v.size());}
  Int go(Int pos,char c){Assert(pos);return v[pos].to[toI(c)];}
  Int go(const string &s,Int pos = 0){
    for(char c:s){
      pos = go(pos,c);
      if(pos == -1) return -1;
    }
    return pos;
  }
  Int back(Int pos){Assert(pos);return v[pos].pre;}
  Int getVal(Int pos){Assert(pos);return v[pos].val;} //0だったら単語が存在しない。
  Int exist(Int pos){Assert(pos);return v[pos].exist;}
  
  void add(const string &s,Int val = 1){
    Int pos = 0;
    for(char c:s){
      if(go(pos,c) != -1){pos = go(pos,c);continue;}
      v.push_back(Node(c,pos));
      pos = v[pos].to[toI(c)] = v.size()-1;
    }
    v[pos].exist = 1;
    v[pos].val = max(v[pos].val, val);  //min,max,+ 臨機応変に変えて
  }
};


Int m;
Trie <26> trie([](char ch){return ch - 'a';});


Int dfs(Int pos,Int len, string his){
  static map<Int,Int> mem[20010];
  his = "";
  if(pos == -1) return 0;
  if(len == m) return trie.exist(pos);
  if(mem[pos].count(len)) return mem[pos][len];
  
  Int res = 0;
  {
    for(char ch='a';ch<='z';ch++){
      Int a =  dfs(trie.go(pos, ch), len+1, his+ch);
      Int b =  trie.exist(pos)? dfs(trie.go(0, ch), len+1, his+ch):0;
      res = min(INF, res + a + b);
      if(res == INF) break;
    }
  }
  return mem[pos][len] = res;
}

Int mult(Int a,Int b){
  Int c = a * b;
  if(a == 0 || b == 0)return 0;
  if(c / b == a && c / a == b) return c;
  return INF;
}

string ans;
void restore(const map<Int,Int> &Pos,Int len,Int K){
  if(len == m) return;
  
  char to = -1;
  Int sum = 0;
  {
    for(char ch = 'a'; ch <= 'z'; ch++){
      Int s = 0;
      for(const auto t:Pos){
	Int pos = t.first;
	Int val = t.second;
	Int a = mult(val , dfs(trie.go(pos, ch), len+1,""));
	Int b = trie.exist(pos)? mult(val , dfs(trie.go(0, ch), len+1,"")):0;
	s = min(INF, s + a + b);
      }
      if(sum + s >= K) {to = ch; break;}
      sum += s;
      
    }
  }

  if(to == -1){ans = "-";return;}
  
  {
    ans += to;
    map<Int,Int> To;
    for(auto t:Pos){
      Int pos = t.first;
      Int val = t.second;
      if(dfs(trie.go(pos, to), len+1, "")) To[trie.go(pos, to)] += val;
      if(trie.exist(pos) && dfs(trie.go(0,to), len+1, "")) To[trie.go(0,to)] += val;
    }
    restore(To, len+1, K - sum);
  }
}

signed main(){
  cin.tie(0);
  ios_base::sync_with_stdio(0);
  cout << fixed << setprecision(12);

  Int n, K;
  cin>>n>>m>>K;

  for(Int i=0;i<n;i++){
    string s;
    cin>>s;
    trie.add(s);
  }

  //pr(dfs(0,0,""));
  map<Int,Int> Pos; Pos[0] = 1;
  restore(Pos, 0, K);
  cout<<ans<<endl;
  return 0;
}
accuracy: 0.7872340425531915
time_ms: 2260
memory_kb: 88452
final_rank: 20

code_snippet: //84104971101048411497 - Can you guess what does this mean?
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef complex<double> point;
#define mapii map<int, int>
#define debug(a) cout << #a << ": " << a << endl
#define debuga1(a, l, r) fto(i, l, r) cout << a[i] << " "; cout << endl
#define fdto(i, r, l) for(int i = (r); i >= (l); --i)
#define fto(i, l, r) for(int i = (l); i <= (r); ++i)
#define forit(it, var) for(__typeof(var.begin()) it = var.begin(); it != var.end(); it++)
#define forrit(rit, var) for(__typeof(var.rbegin()) rit = var.rbegin(); rit != var.rend(); rit++)
#define ii pair<int, int>
#define iii pair<int, ii>
#define ff first
#define ss second
#define mp make_pair
#define pb push_back
#define maxN 105
#define maxM 2005
#define oo 1000000000000000007LL
#define sz(a) (int)a.size()

const double PI = acos(-1.0);

double fRand(double fMin, double fMax)
{
    double f = (double)rand() / RAND_MAX;
    return fMin + f * (fMax - fMin);
}

template <class T>
T min(T a, T b, T c) {
    return min(a, min(b, c));
}

template <class T>
T max(T a, T b, T c) {
    return max(a, max(b, c));
}

ll add(ll &a, const ll &b) {a = min(a+b, oo);}

ll mul(const ll &a, const ll &b) {
    if (a == 0) return 0;
    ll x = a*b;
    if (x/a != b) return oo;
    return min(oo, x);
}

int n, m, z[maxM][maxN];
ll id, cnt[maxM], dp[maxM];
string s[maxN];

int main () {
    scanf("%d%d%lld", &n, &m, &id);
    fto(i, 0, n-1) cin >> s[i];

    cnt[0] = 1;
    fto(i, 1, m) {
        fto(j, 0, n-1) {
            if (i >= sz(s[j])) add(cnt[i], cnt[i-sz(s[j])]);
        }
    }

    dp[0] = 1;
    string ans;
    fto(i, 1, m) {
        vector<ll> sum(26, 0);
        fto(j, 0, n-1) {
            fto(p, max(1, i-sz(s[j])+1), min(i, m-sz(s[j])+1)) {
                if (z[p][j] == i-p) 
                    add(sum[s[j][z[p][j]]-'a'], mul(dp[p-1], cnt[m-(p+sz(s[j])-1)]));                
            }
        }

        fto(c, 'a', 'z') {
            if (sum[c-'a'] >= id) {
                ans += c;
                fto(j, 0, n-1) {
                    fto(p, max(1, i-sz(s[j])+1), i) {
                        if (z[p][j] == i-p && s[j][z[p][j]] == c) ++z[p][j];
                    }
                    if (i >= sz(s[j]) && z[i-sz(s[j])+1][j] == sz(s[j]))
                        add(dp[i], dp[i-sz(s[j])]);
                }

                break;
            } else id -= sum[c-'a'];
        }

        if (sz(ans) != i) {
            puts("-");
            return 0;
        }
    }

    cout << ans << endl;


    return 0;
}
accuracy: 1
time_ms: 400
memory_kb: 4168
final_rank: 4

code_snippet: //84104971101048411497 - Can you guess what does this mean?
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef complex<double> point;
#define mapii map<int, int>
#define debug(a) cout << #a << ": " << a << endl
#define debuga1(a, l, r) fto(i, l, r) cout << a[i] << " "; cout << endl
#define fdto(i, r, l) for(int i = (r); i >= (l); --i)
#define fto(i, l, r) for(int i = (l); i <= (r); ++i)
#define forit(it, var) for(__typeof(var.begin()) it = var.begin(); it != var.end(); it++)
#define forrit(rit, var) for(__typeof(var.rbegin()) rit = var.rbegin(); rit != var.rend(); rit++)
#define ii pair<int, int>
#define iii pair<int, ii>
#define ff first
#define ss second
#define mp make_pair
#define pb push_back
#define maxN 105
#define maxM 2005
#define oo 1000000000000000007LL
#define sz(a) (int)a.size()

const double PI = acos(-1.0);

double fRand(double fMin, double fMax)
{
    double f = (double)rand() / RAND_MAX;
    return fMin + f * (fMax - fMin);
}

template <class T>
T min(T a, T b, T c) {
    return min(a, min(b, c));
}

template <class T>
T max(T a, T b, T c) {
    return max(a, max(b, c));
}

ll add(ll &a, const ll &b) {a = min(a+b, oo);}

ll mul(const ll &a, const ll &b) {
    if (a == 0) return 0;
    if ((a*b)/a != b) return oo;
    return min(oo, a*b);
}

int n, m, z[maxM][maxN];
ll id, cnt[maxM], dp[maxM];
string s[maxN];

int main () {
    scanf("%d%d%lld", &n, &m, &id);
    fto(i, 0, n-1) cin >> s[i];

    cnt[0] = 1;
    fto(i, 1, m) {
        fto(j, 0, n-1) {
            if (i >= sz(s[j])) add(cnt[i], cnt[i-sz(s[j])]);
        }
    }

//    fto(i, 0, m) printf("%lld ", cnt[i]);
//    puts("");

    dp[0] = 1;
    string ans;
    fto(i, 1, m) {
//        debug(i);
        fto(c, 'a', 'z') {
            ll sum = 0;
            fto(j, 0, n-1) {
                fto(p, max(1, i-sz(s[j])+1), min(i, m-sz(s[j])+1)) {
                    if (z[p][j] == i-p && s[j][z[p][j]] == c) {
//                        printf("%d %d\n", j, p);
                        add(sum, mul(dp[p-1], cnt[m-(p+sz(s[j])-1)]));
                        if (sum == oo) break;
                    }
                }
                if (sum == oo) break;
            }
//            printf("%c %lld\n", c, sum);
            if (sum >= id) {
                ans += c;
                fto(j, 0, n-1) {
                    fto(p, max(1, i-sz(s[j])+1), i) {
                        if (z[p][j] == i-p && s[j][z[p][j]] == c) ++z[p][j];
                    }
                    if (i >= sz(s[j]) && z[i-sz(s[j])+1][j] == sz(s[j]))
                        add(dp[i], dp[i-sz(s[j])]);
                }

                break;
            } else id -= sum;
        }

        if (sz(ans) != i) {
            puts("-");
            return 0;
        }
    }

    cout << ans << endl;


    return 0;
}
accuracy: 1
time_ms: 1470
memory_kb: 4124
final_rank: 9