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: Accepted)

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

using uint=unsigned;
using ll=long long;
using ull=unsigned long long;
//#define int ll

#define FOR(i,a,b) for(int i=int(a);i<int(b);i++)
#define rep(i,b) FOR(i,0,b)
#define ROF(i,a,b) for(int i=int(b)-1;i>=a;i--)
#define per(i,b) ROF(i,0,b)
#define mp make_pair
#define mt make_tuple
#define pb push_back
#define eb emplace_back
#define all(x) x.begin(),x.end()
auto& errStream=cerr;
#ifdef LOCAL
#define cerr (cerr<<"-- line "<<__LINE__<<" -- ")
#else
class CerrDummy{}cerrDummy;
template<class T>
CerrDummy& operator<<(CerrDummy&cd,const T&){
	return cd;
}
using charTDummy=char;
using traitsDummy=char_traits<charTDummy>;
CerrDummy& operator<<(CerrDummy&cd,basic_ostream<charTDummy,traitsDummy>&(basic_ostream<charTDummy,traitsDummy>&)){
	return cd;
}
#define cerr cerrDummy
#endif
#define reach cerr<<"reached"<<endl
void dmpr(decltype(cerr)&os){os<<endl;}
template<class T,class... Args>
void dmpr(decltype(cerr)&os,const T&t,const Args&... args){
	os<<t<<" ";
	dmpr(os,args...);
}
#define dmp(...) dmpr(cerr,##__VA_ARGS__)
#define zero(x) memset(x,0,sizeof(x))
#define one(x) memset(x,-1,sizeof(x))
#define fs first
#define sc second
#define bg begin()
#define ed end()

template<class T> using V=vector<T>;
template<class T> using VV=V<V<T>>;

using pi=pair<int,int>;
using vi=vector<int>;
using ld=long double;

template<class T,class U>
ostream& operator<<(ostream& os,const pair<T,U>& p){
	os<<"("<<p.first<<","<<p.second<<")";
	return os;
}

template<class T>
ostream& operator <<(ostream& os,const vector<T>& v){
	os<<"{";
	rep(i,(int)v.size()){
		if(i)os<<",";
		os<<v[i];
	}
	os<<"}";
	return os;
}

template<int i,class T>
void print_tuple(ostream&,const T&){
}

template<int i,class T,class H,class ...Args>
void print_tuple(ostream&os,const T&t){
	if(i)os<<",";
	os<<get<i>(t);
	print_tuple<i+1,T,Args...>(os,t);
}

template<class ...Args>
ostream& operator<<(ostream&os,const tuple<Args...>&t){
	os<<"(";
	print_tuple<0,tuple<Args...>,Args...>(os,t);
	return os<<")";
}

ll read(){
	ll i;
	scanf("%lld",&i);
	return i;
}

void printSpace(){
	printf(" ");
}

void printEoln(){
	printf("\n");
}

void print(ll x,int suc=1){
	printf("%lld",x);
	if(suc==1)
		printEoln();
	if(suc==2)
		printSpace();
}

vi readvi(int n,int off=0){
	vi v(n);
	rep(i,n)v[i]=read()+off;
	return v;
}

template<class T>
void print(const vector<T>&v,int suc=1){
	rep(i,v.size())
		print(v[i],i==int(v.size())-1?suc:2);
}

string readString(){
	static char buf[3341000];
	scanf("%s",buf);
	return string(buf);
}

char* readCharArray(){
	static char buf[3341000];
	static int bufUsed=0;
	char* ret=buf+bufUsed;
	scanf("%s",ret);
	bufUsed+=strlen(ret)+1;
	return ret;
}

template<class T,class U>
void chmax(T& a,U b){
	if(a<b)
		a=b;
}

template<class T,class U>
void chmin(T& a,U b){
	if(b<a)
		a=b;
}

template<class T>
T Sq(const T& t){
	return t*t;
}

//#define CAPITAL
void Yes(bool ex=true){
	#ifdef CAPITAL
	cout<<"YES"<<endl;
	#else
	cout<<"Yes"<<endl;
	#endif
	if(ex)exit(0);
}
void No(bool ex=true){
	#ifdef CAPITAL
	cout<<"NO"<<endl;
	#else
	cout<<"No"<<endl;
	#endif
	if(ex)exit(0);
}

const ll infLL=LLONG_MAX/3;

#ifdef int
const int inf=infLL;
#else
const int inf=INT_MAX/2-100;
#endif

constexpr ll TEN(int n){
	return n==0?1:TEN(n-1)*10;
}

template<class T>
vector<T> uni(const vector<T>&vv){
	auto v(vv);
	sort(all(v));
	v.erase(unique(all(v)),v.end());
	return v;
}
template<class T>
void mkuni(vector<T>&v){
	sort(all(v));
	v.erase(unique(all(v)),v.end());
}

//ayasii
int topbit(signed t){
	return t==0?-1:31-__builtin_clz(t);
}
int topbit(ll t){
	return t==0?-1:63-__builtin_clzll(t);
}
int popcount(signed t){
	return __builtin_popcount(t);
}
int popcount(ll t){
	return __builtin_popcountll(t);
}
bool ispow2(int i){
	return i&&(i&-i)==i;
}

bool inc(int a,int b,int c){
	return a<=b&&b<=c;
}
struct SuffixArray{
	vi sa,as,lcp;
};
namespace SA_IS{
	const int Nmax=1000010;
	int bucket[Nmax];
	template <class T>
	void CreateBeginBucket(const T* data, int size, int maxVal){
		rep(i, maxVal + 1) bucket[i] = 0;
		rep(i, size) bucket[(int)data[i]]++;
		int sum = 0;
		rep(i, maxVal + 1){ bucket[i] += sum; swap(bucket[i], sum); }
	}
	 
	template <class T>
	void CreateEndBucket(const T* data, int size, int maxVal){
		rep(i, maxVal + 1) bucket[i] = 0;
		rep(i, size) bucket[(int)data[i]]++;
		int sum = 0;
		rep(i, maxVal + 1){ sum += bucket[i]; bucket[i] = sum; }
	}
	 
	template<class T>
	void InducedSort(const T* data, int size, int* SA, int maxVal, bool* isL){
		CreateBeginBucket(data, size, maxVal);
		rep(i, size) if (SA[i] > 0 && isL[SA[i] - 1]) SA[bucket[(int)data[SA[i] - 1]]++] = SA[i] - 1;
	}
	 
	template<class T>
	void InvertInducedSort(const T* data, int size, int* SA, int maxVal, bool* isL){
		CreateEndBucket(data, size, maxVal);
		for (int i = size - 1; i >= 0; --i) if (SA[i] > 0 && !isL[SA[i] - 1]) SA[--bucket[(int)data[SA[i] - 1]]] = SA[i] - 1;
	}
	 
	template <class T>
	void DBGOUT(const T* sa, int size){
		rep(i, size) printf("%d ", int(sa[i]));
		printf("\n");
	}
	 
	template<class T>
	void SA_IS(const T* data, int size, int* SA, int maxVal, bool* isL){
		rep(i, size) SA[i] = -1;
	#define isLMS(x) (x>0 && isL[x-1] && !isL[x])
		isL[size - 1] = false;
		for (int i = size - 2; i >= 0; i--) isL[i] = data[i] > data[i + 1] || (data[i] == data[i + 1] && isL[i + 1]);
		CreateEndBucket(data, size, maxVal);
		FOR(i, 1, size) if (isLMS(i)) SA[--bucket[(int)data[i]]] = i;
		InducedSort(data, size, SA, maxVal, isL);
		InvertInducedSort(data, size, SA, maxVal, isL);
	 
		int c = 0;
		rep(i, size) if (isLMS(SA[i])) SA[c++] = SA[i];
		FOR(i, c, size) SA[i] = -1;
	 
		int idx = -1;
		int prev = -1;
		rep(i, c){
			bool diff = false;
			rep(d, size){
				if (prev == -1 || data[SA[i] + d] != data[prev + d] || isL[SA[i] + d] != isL[prev + d]){
					diff = true;
					break;
				}
				else if (d > 0 && isLMS(SA[i] + d)) break;
			}
			if (diff){ idx++; prev = SA[i]; }
			SA[c + SA[i] / 2] = idx;
		}
		int j = size;
		for (int i = size - 1; i >= c; i--) if (SA[i] >= 0) SA[--j] = SA[i];
	 
		int* nxdata = SA + size - c;
		int* nxsa = SA;
		if (c == idx + 1) rep(i, c) nxsa[nxdata[i]] = i;
		else SA_IS(nxdata, c, nxsa, idx, isL + size);
	 
		j = c;
		for (int i = size - 1; i >= 1; i--) if (isLMS(i)) nxdata[--j] = i;
		rep(i, c) nxsa[i] = nxdata[nxsa[i]];
		FOR(i, c, size) SA[i] = -1;
		CreateEndBucket(data, size, maxVal);
		for (int i = c - 1; i >= 0; i--) swap(nxsa[i], SA[--bucket[(int)data[nxsa[i]]]]);
		InducedSort(data, size, SA, maxVal, isL);
		InvertInducedSort(data, size, SA, maxVal, isL);
	#undef isLMS
	}
	 
	bool isLPool[Nmax * 2];
	template<class T>
	void SA_IS(const T* data, int size, int* SA){
		int mv = 0;
		rep(i, size) if (mv < data[i]) mv = data[i];
		SA_IS(data, size, SA, mv, isLPool);
	}
	 
	int sa[Nmax],as[Nmax],lcp[Nmax];
	template<class T>
	void CreateLCP(const T* data, int size){
		rep(i,size) as[sa[i]]=i;
		int same=0;
		rep(i,size){
			if(as[i]<size-1){
				int j=sa[as[i]+1];
				while(i+same<size&&j+same<size&&data[i+same]==data[j+same])
					same++;
			}
			lcp[as[i]]=same;
			same=max(same-1,0);
		}
	}
	
	//last element should be 0(as a sentinel)
	//n is the length of the data(including the sentinel)
	//sa[i] index of i'th smallest suffix
	//lcp[i] lcp(sa[i],sa[i+1])
	template<class T>
	SuffixArray BuildSA(const T* data,int n){
		fill(bucket,bucket+n,0);
		fill(isLPool,isLPool+2*n,false);
		fill(sa,sa+n,0);
		fill(as,as+n,0);
		fill(lcp,lcp+n,0);
		SA_IS(data,n,sa);
		CreateLCP(data,n);
		return SuffixArray{vi(sa,sa+n),vi(as,as+n),vi(lcp,lcp+n)};
	}
}

template<class T,class F>
struct SparseTable{
	vector<vector<T>> st;
	const F&f;
	SparseTable(const V<T>& data,F ff):f(ff){
		if(data.empty())
			return;
		int n=data.size(),h=topbit(n);
		st.resize(h+1);
		st[0]=data;
		FOR(j,1,h+1){
			st[j].resize(n-(1<<j)+1);
			rep(i,n-(1<<j)+1)
				st[j][i]=f(st[j-1][i],st[j-1][i+(1<<(j-1))]);
		}
	}
	T get(int b,int e){
		assert(b<e);
		int d=topbit(e-b);
		return f(st[d][b],st[d][e-(1<<d)]);
	}
};
template<class T,class F>
SparseTable<T,F> getST(const V<T>& data,F f){
	return SparseTable<T,F>(data,f);
}

const int S=1000010;
const int N=2010;
const int K=10010;

char cat[S+N];
bool dp[N][K];

signed main(){
	int n=read(),k=read();
	int sum=0;
	V<string> s;
	vi pos(n);
	rep(i,n){
		s.pb(readString());
		pos[i]=sum;
		for(auto c:s[i])
			cat[sum++]=c;
		cat[sum++]='a'-1;
	}
	auto sa=SA_IS::BuildSA(cat,sum+1);
	auto st=getST(sa.lcp,[&](int a,int b){return min(a,b);});
	auto lcp=[&](pi a,pi b){
		return st.get(sa.as[pos[a.fs]+a.sc],sa.as[pos[b.fs]+b.sc]);
	};
	auto cmp=[&](pi a,pi b){
		return sa.as[pos[a.fs]+a.sc]<sa.as[pos[b.fs]+b.sc];
	};
	
	dp[n][0]=1;
	per(i,n){
		rep(j,k+1){
			dp[i][j]=dp[i+1][j];
			if(j>=int(s[i].size()))
				dp[i][j]|=dp[i+1][j-s[i].size()];
		}
	}
	
	vi idx(n);
	iota(all(idx),0);
	sort(all(idx),[&](int a,int b){
		return sa.as[pos[a]]<sa.as[pos[b]];
	});
	
	V<pi> cur,nx;
	string ans;
	int mn=0;
	rep(i,k){
		nx.clear();
		auto push=[&](pi b){
			if(nx.size()){
				pi a=nx.back();
				int x=s[a.fs].size()-a.sc;
				int y=min(x,lcp(a,b));
				if(x>y)return;
				if(x==y&&x==int(s[b.fs].size()-b.sc)){
					chmin(nx.back(),b);
					return;
				}
			}
			nx.pb(b);
		};
		int p=0;
		for(auto j:idx)if(mn<=j&&i+int(s[j].size())<=k&&dp[j+1][k-i-s[j].size()]){
			while(p<int(cur.size())&&cmp(cur[p],pi(j,0)))
				push(cur[p++]);
			push(pi(j,0));
		}
		while(p<int(cur.size()))
			push(cur[p++]);
		cur.clear();
		ans+=s[nx[0].fs][nx[0].sc];
		mn=inf;
		for(auto w:nx){
			if(w.sc+1==int(s[w.fs].size())){
				mn=w.fs+1;
			}else{
				cur.eb(w.fs,w.sc+1);
			}
		}
	}
	
	cout<<ans<<endl;
}