#include<deque>
#include<queue>
#include<vector>
#include<algorithm>
#include<iostream>
#include<set>
#include<cmath>
#include<tuple>
#include<string>
#include<chrono>
#include<functional>
#include<iterator>
#include<random>
#include<unordered_set>
#include<unordered_map>
#include<array>
#include<map>
#include<bitset>
#include<iomanip>
#include<list>
#include <numeric>
using namespace std;
typedef unsigned long long int ulint;
typedef long long int llint;
typedef long double lldo;
#define mp make_pair
#define mt make_tuple
#define pub push_back
#define puf push_front
#define pob pop_back
#define pof pop_front
#define fir first
#define sec second
#define res resize
#define ins insert
#define era erase
#define RE return 0
//ios::sync_with_stdio(false);
//std::cin.tie(0);
//<< setprecision(20)
const int mod=(int)1e9+7;
const llint big=(llint)1e17+100;
const long double pai=3.141592653589793238462643383279502884197;
const long double ena=2.71828182845904523536;
const long double eps=1e-7;
template <class T,class U>void mineq(T& a,U b){if(a>b){a=b;}}
template <class T,class U>void maxeq(T& a,U b){if(a<b){a=b;}}
template <class T> void soun(T& ar)
{sort(ar.begin(),ar.end());ar.erase(unique(ar.begin(),ar.end()),ar.end());}
llint gcd(llint a,llint b){if(a%b==0){return b;}else{return gcd(b,a%b);}}
llint lcm(llint a,llint b){return a/gcd(a,b) *b;}
template<class T,class U> auto LB(T& ve,U in){return lower_bound(ve.begin(),ve.end(),in);}
template<class T,class U> auto UB(T& ve,U in){return upper_bound(ve.begin(),ve.end(),in);}
template<class T,class U> auto LBI(T& ve,U in){return LB(ve,in)-ve.begin();}
template<class T,class U> auto UBI(T& ve,U in){return UB(ve,in)-ve.begin();}
template<class T> void SO(T& ve){sort(ve.begin(),ve.end());}
template<class T> void REV(T& ve){reverse(ve.begin(),ve.end());}
//前の文字列のうち,遷移可能なところで切って、文字列を置き換える
//その最小を求める
//O(s+K)で任意の0<=i<s.size()について、kとs[i]以降の何文字一致しているか を求めたい
//Z-algo
//suffix-Array from ei13333
//接尾辞配列。蟻本には O(Nlog2N)の実装が載っているが, 
//ここでは O(NlogN)の実装を扱う。
//SA-IS を使えば線形時間で構築できる。
struct SuffixArray{
	vector<int> SA;
	string s;
	void Build_SA(const string& str){
		s = str;
		SA.resize(s.size());
		iota(begin(SA), end(SA), 0);
		sort(begin(SA), end(SA), [&](const int& a, const int& b)
		{
			if(s[a] == s[b]) {return(a > b);}
			return (s[a] < s[b]);
		});
		vector< int > classes(s.size()), c(s.size()), cnt(s.size());
		for(int i = 0; i < s.size(); i++) {c[i] = s[i];}
		for(int len = 1; len < s.size(); len <<= 1) {
			for(int i = 0; i < s.size(); i++) {
				if(i > 0 && c[SA[i - 1]] == c[SA[i]] && SA[i - 1] + len < s.size() && c[SA[i - 1] + len / 2] == c[SA[i] + len / 2]) {
					classes[SA[i]] = classes[SA[i - 1]];
				} else {
					classes[SA[i]] = i;
				}
			}
			iota(begin(cnt), end(cnt), 0);
			copy(begin(SA), end(SA), begin(c));
			for(int i = 0; i < s.size(); i++) {
				int s1 = c[i] - len;
				if(s1 >= 0) SA[cnt[classes[s1]]++] = s1;
			}
			classes.swap(c);
		}
	}
	int operator[](int k) const{return (SA[k]);}
	int size() const{return (s.size());}
	bool lt_substr(string& t, int si = 0, int ti = 0)
	{
		int sn = s.size(), tn = t.size();
		while(si < sn && ti < tn) {
			if(s[si] < t[ti]) return (true);
			if(s[si] > t[ti]) return (false);
			++si, ++ti;
		}
		return (si >= sn && ti < tn);
	}
	int lower_bound(string& t)
	{
		int low = -1, high = SA.size();
		while(high - low > 1) {
			int mid = (low + high) >> 1;
			if(lt_substr(t, SA[mid])) low = mid;
			else high = mid;
		}
		return (high);
	}
	pair< int, int > lower_upper_bound(string& t)
	{
		int idx = lower_bound(t);
		int low = idx - 1, high = SA.size();
		t.back()++;
		while(high - low > 1) {
			int mid = (low + high) >> 1;
			if(lt_substr(t, SA[mid])) low = mid;
			else high = mid;
		}
		t.back()--;
		return (make_pair(idx, high));
	}
	void output()
	{
		for(int i = 0; i < size(); i++) {
			cout << i << ": " << s.substr(SA[i]) << endl;
		}
	}
};
//from snuke
vector<int>Zalgo(string S){
	vector<int>A(S.size());
	int c=0;
	for(int i = 1; i < S.size(); i++){
		if (i+A[i-c] < c+A[c]) {A[i] = A[i-c];}
		else {
			int j = max(0, c+A[c]-i);
			while(i+j<S.size()&&S[j]==S[i+j]){j++;}
			A[i]=j;c=i;
		}
	}
	A[0]=S.size();
	return A;
}
int aaaa;
int main(void){
	int i,j,n,K;cin>>n>>K;
	vector<bitset<10001>> can(n);//これがtrueなら遷移可能
	vector<string> str(n);
	for(i=0;i<n;i++){cin>>str[i];}
	bitset<10001> gen;gen[K]=true;
	for(i=n-1;i>=0;i--){
		can[i]=(gen>>(str[i].size()));
		gen|=(gen>>(str[i].size()));
	}
	string ans;ans.pub('~');//晩兵
	bitset<10001> nai;
	bitset<10001>full;
	for(i=0;i<=10000;i++){full[i]=true;}
	gen=nai;//0に初期化
	gen[0]=true;
	for(i=0;i<n;i++){
		//cerr<<"sta"<<__LINE__<<endl;
		//for(j=0;j<=15;j++){cerr<<gen[j];}cerr<<endl;
		string Z=str[i];
		int s=str[i].size();
		Z.pub('|');
		Z+=ans;
		auto iti=Zalgo(Z);//一致
		int hen=mod;//変更される場所の最小値
		vector<int>mto;//henをみたす遷移元
		for(j=0;j<ans.size();j++){
			if(!can[i][j]){continue;}
			if(!gen[j]){continue;}
			//cerr<<"de j="<<j<<" "<<iti[j+s+1]<<endl;
			if(iti[j+s+1]==s){gen[j+s]=true;continue;}//完全に一致
			if(str[i][iti[j+s+1]]>ans[j+iti[j+s+1]]){continue;}//だめだ。こっちの方がでかい
			//cerr<<"de j="<<j<<" "<<iti[j+s+1]<<endl;
			if(hen<j+iti[j+s+1]){continue;}//変更量がちいさすぎる。
			if(hen>j+iti[j+s+1]){hen=j+iti[j+s+1];mto.clear();}//こっちの変更がでかいのでこれまでにさよなら
			mto.pub(j);
		}
		if(hen==mod){continue;}
		//cerr<<"de"<<__LINE__<<endl;
		while(ans.size()>hen){ans.pob();}
		str[i].pub('~');//文字数をできるだけ大きくするため
		//it=mto[j]; str[i]のhen-it以降の文字列 の辞書順最小をもとめればよい
		//ただし、接頭辞が同じなら、両方を選ぶ
		vector<bool>have(str[i].size());
		for(auto it:mto){have[hen-it]=true;}
		SuffixArray usi;
		usi.Build_SA(str[i]);
		string kou;
		//cerr<<"de"<<__LINE__<<endl;
		for(j=0;j<usi.size();j++){
			if(!have[usi[j]]){continue;}
			for(int k=usi[j];k<str[i].size();k++){kou.pub(str[i][k]);}
			break;
		}
		ans+=kou;
		kou.back()='|';
		int kos=kou.size();
		kou+=str[i];
		auto kouz=Zalgo(kou);
		//cerr<<"de"<<__LINE__<<endl;
		//cerr<<"hen+kos="<<hen<<endl;
		gen&=(full>>(10000-hen));
		//for(j=0;j<=15;j++){cerr<<gen[j];}cerr<<endl;
		//cerr<<kou<<endl;
		//cerr<<kouz[kos]<<endl;
		for(j=0;j+1<str[i].size();j++){
			if(hen-j<0){continue;}
			if(!have[j]){continue;}
			//cerr<<"j="<<j<<"aa="<<hen-j+(int)str[i].size()-1<<endl;
			if(kouz[j+kos]==(int)kou.size()-j-kos-1){gen[hen-j+(int)str[i].size()-1]=true;}
		}
		//cerr<<"ans="<<ans<<endl;
		//for(j=0;j<=15;j++){cerr<<gen[j];}cerr<<endl;
		//cerr<<"de"<<__LINE__<<endl;
	}
	ans.pob();cout<<ans<<endl;
}

Measured memory_bytes: 4352