Problem:
Score : 1500 points

Problem Statement

Iroha has a sequence of N strings s_1, s_2, ..., s_N.

She will choose some (possibly all) strings from the sequence, then concatenate those strings retaining the relative order, to produce a long string.

Among all strings of length K that she can produce in this way, find the lexicographically smallest one.

Constraints

1 ≦ N ≦ 2000

1 ≦ K ≦ 10^4

For each i, 1 ≦ |s_i| ≦ K.

|s_1| + |s_2| + ... + |s_N| ≦ 10^6

For each i, s_i consists of lowercase letters.

There exists at least one string of length K that Iroha can produce.

Input

The input is given from Standard Input in the following format:

N K
s_1
s_2
:
s_N

Output

Print the lexicographically smallest string of length K that Iroha can produce.

Sample Input 1

3 7
at
coder
codar

Sample Output 1

atcodar

at and codar should be chosen.

Sample Input 2

3 7
coder
codar
at

Sample Output 2

codarat

codar and at should be chosen.

Sample Input 3

4 13
kyuri
namida
zzzzzzz
aaaaaa

Sample Output 3

namidazzzzzzz

namida and zzzzzzz should be chosen.

Solution in C++14 (GCC 5.4.1) (judge verdict: Wrong Answer)

#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ull;

const int maxn = 2e3 + 505;
const int maxm = 1e4 + 505;
const ull seed = 233;

int n, k, len[maxn];
ull Hash[maxm], cur[maxm], fac[maxn * 505];

char ex[maxm];
string s[maxn], st[maxn];

bool Rm[maxn][maxm], ok[2][maxm];

inline int read() {
	char ch = getchar(); int u = 0, f = 1;
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) {u = u * 10 + ch - 48;  ch = getchar();} return u * f;
}

inline int checkit(string &st, string &s, int pos, int j) {
	int l = 1, r = min(pos, j) + s.size(), R = 0, mid = (l + r) / 2; R = r;
	int siz = s.size(), ans = 0, ch0, ch1;
	while(l <= r) {
		mid = (l + r) / 2;
		ull H0 = 0, H1 = 0;
		if(mid <= pos) H0 = cur[mid];
		else H0 = cur[pos] * fac[mid - pos] + Hash[mid - pos];
		if(mid <= j)   H1 = cur[mid];
		else H1 = cur[j] * fac[mid - j] + Hash[mid - j];
		if(H0 == H1) l = mid + 1, ans = mid;
		else r = mid - 1;
	}
	if(ans == R) return j > pos ? j : pos;
	else {
		if(ans >= pos) ch0 = s[ans - pos]; else ch0 = st[ans];
		if(ans >= j) ch1 = s[ans - j]; else ch1 = st[ans];
		return ch0 > ch1 ? j : pos;
	}
}

inline string checkagain(string &st, string &now, string &s, int pos) {
	int l = 1, r = min(now.size(), st.size()), R = 0, mid = (l + r) / 2; R = r;
	int siz = s.size(), ans = 0, ch0, ch1;
	while(l <= r) {
		mid = (l + r) / 2;
		ull H0 = 0, H1 = 0;
		H0 = cur[mid];
		if(mid <= pos) H1 = cur[mid];
		else H1 = cur[pos] * fac[mid - pos] + Hash[mid - pos];
		if(H0 == H1) l = mid + 1, ans = mid;
		else r = mid - 1;
	}
	if(ans == R) {
        if(st.size() > now.size()) return st;
        else return now;
	}
	else {
		ch0 = now[ans]; ch1 = st[ans];
		if(ch0 > ch1) return st;
		else return now;
	}
}

int main() {
	//freopen("MY", "w", stdout);
	n = read(); k = read(); fac[0] = 1;

	for(register int i = 1; i <= maxn * 500; i++)
		fac[i] = fac[i - 1] * seed;

	for(register int i = 1; i <= n; i++) {
		scanf("%s", ex); s[i] = ex;
	}

	Rm[n + 1][0] = 1;
	for(register int i = n; i >= 1; i--)
		for(register int j = 0; j <= k; j++) {
			if(j >= s[i].size()) Rm[i][j] |= Rm[i + 1][j - s[i].size()];
			Rm[i][j] |= Rm[i + 1][j];
		}

	if(Rm[2][k - s[1].size()]) {
		ok[0][1] = 1; st[1] = s[1];
	}

	for(register int i = 2; i <= n; i++) {
		int t = i & 1, t0 = t ^ 1, p = -1;
		memset(ok[t0], 0, sizeof(ok[t0]));

		cur[0] = Hash[0] = 0;
		for(register int j = 1; j <= st[i - 1].size(); j++)
			cur[j] = cur[j - 1] * seed + st[i - 1][j - 1];
		for(register int j = 1; j <= s[i].size(); j++)
			Hash[j] = Hash[j - 1] * seed + s[i][j - 1];

		if(i == 90)
            int kk = 0;

		for(register int j = 1; j <= st[i - 1].size() + 1; j++) {
			if(ok[t][j]) ok[t0][j] = 1;
			if(!Rm[i + 1][k - (j + s[i].size() - 1)] || !(ok[t][j] || j > st[i - 1].size())) continue;
			//if(gethash(j, i)) ok[t0][j + s[i].size()] = 1;
			if(p == -1) p = j;
			else p = checkit(st[i - 1], s[i], p - 1, j - 1) + 1;
		}

        if(p != -1)
            st[i] = st[i - 1].substr(0, p - 1) + s[i];
        else st[i] = st[i - 1];

		if(Rm[i + 1][k - st[i - 1].size()])
			st[i] = checkagain(st[i - 1], st[i], s[i], p - 1);

		if(st[i] == st[i - 1]) p = st[i].size() + 1;

		ok[t0][p] = 1;
		/*for(register int j = p + 1; j <= k; j++) {
			if(j <= st[i - 1].size() + 1 && ok[t0][j]) {
				ull H0 = cur[j - 1];
				ull H1 = cur[p - 1] * fac[j - p] + Hash[j - p];
				ok[t0][j] = (H0 == H1);
			}
			else ok[t0][j] = 0;
		}

		for(register int j = 1; j <= st[i].size(); j++) {
            int o = j + s[i].size() - 1, z = st[i - 1].size();
            if(o > st[i].size()) break;
            if(!Rm[i + 1][k - o] || !ok[t][j]) continue;
            ull H0 = 0, H1 = 0;
            if(o > st[i - 1].size()) H0 = cur[z] * fac[o - z] + Hash[o - z];
            else H0 = cur[o];
            H1 = cur[j - 1] * fac[s[i].size()] + Hash[s[i].size()];
            if(H0 == H1) ok[t0][o + 1] = 1;
		}*/

		/*cerr << "case" << i << ": " << endl;
		cerr << st[i] << endl;
		for(register int j = 1; j <= k; j++)
			cerr << ok[t0][j] << " ";
		cerr << "\n\n";*/
	}

	cout << st[n];
	return 0;
}
/*
*/