//  Copyright 2020 Nikita Golikov

#include <bits/stdc++.h>

using namespace std;

using i64 = int64_t;
using ui64 = uint64_t;

#ifdef GOLIKOV
mt19937_64 rng(566);
#else
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
#endif

template <typename T>
T rnd_helper(T from, T to, true_type) {
  return uniform_int_distribution<T>(from, to)(rng);
}

template <typename T>
T rnd_helper(T from, T to, false_type) {
  return uniform_real_distribution<T>(from, to)(rng);
}

template <typename T>
T rnd(T from, T to) {
  return rnd_helper(from, to, is_integral<T>{});
}

template <typename A, typename B, typename Cmp>
bool set_min(A &x, B const &y, Cmp &&cmp) {
  if (cmp(y, x)) {
    x = y;
    return true;
  }
  return false;
}

template <typename A, typename B>
bool set_min(A &x, B const &y) {
  return set_min(x, y, less<typename common_type<A, B>::type>{});
}

template <typename A, typename B>
bool set_max(A &x, B const &y) {
  return set_min(x, y, greater<typename common_type<A, B>::type>{});
}

#include <ext/pb_ds/assoc_container.hpp>

using namespace __gnu_pbds;

template <typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
                         tree_order_statistics_node_update>;

template <typename K, typename V>
using ordered_map = tree<K, V, less<K>, rb_tree_tag,
                         tree_order_statistics_node_update>;

//  https://codeforces.com/blog/entry/62393
struct custom_hash {
  static uint64_t splitmix64(uint64_t x) {
    // http://xorshift.di.unimi.it/splitmix64.c
    x += 0x9e3779b97f4a7c15;
    x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
    x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
    return x ^ (x >> 31);
  }

  size_t operator()(uint64_t x) const {
    static const uint64_t
        FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
    return static_cast<size_t>(splitmix64(x + FIXED_RANDOM));
  }
};

#ifdef __cpp_deduction_guides
template <typename F>
struct rec_fun {
  F fun;

  rec_fun(F&& fun_) : fun(std::forward<F>(fun_)) {}

  template <typename... Args>
  decltype(auto) operator()(Args&&... args) const {
    return fun(*this, std::forward<Args>(args)...);
  }
};
#endif

#ifdef __cpp_inline_variables
template <typename T>
inline constexpr auto type_min = numeric_limits<T>::min();

template <typename T>
inline constexpr auto type_max = numeric_limits<T>::max();
#endif

//  fast alloc
#if 0
int const MAX_MEM = 1e8;
int mpos = 0;
char mem[MAX_MEM];
void* operator new(size_t n) {
  mpos += n;
  return (void*)(mem + mpos - n);
}
void operator delete (void*) noexcept {}
#endif

//  pragmas
#if 0
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4")
#pragma GCC optimize("unroll-loops")
#endif

vector<int> get_z(string const& s) {
  int const n = (int) s.size();
  vector<int> z(n);
  for (int i = 1, l = 0, r = 0; i < n; ++i) {
    int k = max(0, min(z[i - l], r - i));
    while (i + k < n && s[k] == s[i + k]) ++k;
    if (i + k > r) {
      l = i;
      r = i + k;
    }
    z[i] = k;
  }
  return z;
}

int main() {
#ifdef GOLIKOV
  assert(freopen("in", "rt", stdin));
  auto _clock_start = chrono::high_resolution_clock::now();
#endif
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  int n, k;
  cin >> n >> k;
  vector<string> a(n);
  for (auto& x : a) {
    cin >> x;
  }
  vector<vector<int>> can(n + 1, vector<int>(k + 1));
  can[n][0] = 1;
  for (int i = n - 1; i >= 0; --i) {
    int const cur = (int) a[i].size();
    for (int len = 0; len <= k; ++len) {
      can[i][len] = can[i + 1][len];
      if (len >= cur) {
        can[i][len] |= can[i + 1][len - cur];
      }
    }
  }

  char const END = 0;

  vector<int> alive(k + 1);
  int const DEAD = 0;
  int const PREF = 1;
  int const CONCAT = 2;
  alive[0] = PREF;
  string s;
  for (int i = 0; i < n; ++i) {
    auto const& t = a[i];
    int const len_t = (int) t.size();
    auto z_t = get_z(t);
    auto big = t;
    big.push_back('#');
    big += s;
    auto z_big = get_z(big);
    auto lcp_t = [&](int suff) {
      if (suff >= len_t) return 0;
      if (!suff) return len_t;
      return z_t[suff];
    };
    auto lcp_st = [&](int suff) {
      return z_big[len_t + 1 + suff];
    };

    auto get_char = [&](int pref, int take, int pos) -> char {
      if (pos < pref) {
        return s[pos];
      }
      if (!take) {
        return END;
      }
      pos -= pref;
      return pos < len_t ? t[pos] : END;
    };

    auto find_lcp = [&](int l1, int t1, int l2, int t2) {
      if (l1 > l2) {
        swap(l1, l2);
        swap(t1, t2);
      }
      if (!t1) {
        return l1;
      }
      auto lcp = lcp_st(l1);
      if (!t2 || lcp == len_t || lcp < l2 - l1) {
        return l1 + lcp;
      }
      return l1 + lcp + lcp_t(lcp);
    };

    auto new_alive = alive;
    for (int len = 0; len + len_t <= k; ++len) {
      if (!alive[len]) {
        continue;
      }
      if (!can[i + 1][k - len - len_t]) {
        continue;
      }
      if (!alive[len + len_t]) {
        new_alive[len + len_t] = CONCAT;
        continue;
      }
      auto lcp = find_lcp(len, 1, len + len_t, 0);
      if (get_char(len, 1, lcp) < get_char(len + len_t, 0, lcp)) {
        new_alive[len + len_t] = CONCAT;
      }
    }
    int last = -1;
    for (int len = k; len >= 0; --len) {
      auto t1 = new_alive[len];
      if (t1 == DEAD) {
        continue;
      }
      if (last < 0) {
        last = len;
        continue;
      }
      auto t2 = new_alive[last];
      auto has1 = t1 == CONCAT;
      auto has2 = t2 == CONCAT;
      auto pref1 = has1 ? len - len_t : len;
      auto pref2 = has2 ? last - len_t : last;
      auto lcp = find_lcp(pref1, has1, pref2, has2);
      if (lcp == len) {
        continue;
      }
      if (get_char(pref1, has1, lcp) < get_char(pref2, has2, lcp)) {
        last = len;
      } else {
        new_alive[len] = DEAD;
      }
    }
    for (int l = last + 1; l <= k; ++l) {
      alive[l] = DEAD;
    }
    if (new_alive[last] == CONCAT) {
      s.resize(last - len_t);
      s += t;
    }
    for (int l = last; l >= 0; --l) {
      if (new_alive[l] != DEAD) {
        alive[l] = PREF;
      } else {
        alive[l] = DEAD;
      }
    }
  }
  cout << s << '\n';

#ifdef GOLIKOV
  cerr << "Executed in " << chrono::duration_cast<chrono::milliseconds>(
      chrono::high_resolution_clock::now()
          - _clock_start).count() << "ms." << endl;
#endif
  return 0;
}


Measured memory_bytes: 79616