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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K;
	auto dp = make_v<ll>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	string ans;
	REP(i, msum) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	int cnt = 0;
	bool current = 0;
	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum >= (i + j)) {
				q[current].push_back({i, j});
			}
		}
	}
	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N) chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N) chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and f[p.first + 1][p.second] == mc)
				q[next].push_back({p.first + 1, p.second});
			if (p.second + 1 < N and f[p.first][p.second + 1] == mc)
				q[next].push_back({p.first, p.second + 1});
		}
		q[current].clear();
		current = not current;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K;
	auto dp = make_v<int>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	string ans;
	if (K == 0) ans.push_back(f[0][0]);
	REP(i, msum) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	auto used = make_v<bool>(N, N);
	int cnt = 0;
	bool current = 0;
	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum >= (i + j)) {
				q[current].push_back({i, j});
				used[i][j] = true;
			}
		}
	}
	dp.clear();
	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second])
				chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N and not used[p.first][p.second + 1])
				chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and f[p.first + 1][p.second] == mc) {
				used[p.first + 1, p.second];
				q[next].push_back({p.first + 1, p.second});
			}
			if (p.second + 1 < N and f[p.first][p.second + 1] == mc) {
				used[p.first, p.second + 1];
				q[next].push_back({p.first, p.second + 1});
			}
		}
		q[current].clear();
		current = not current;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K;
	auto dp = make_v<int>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	string ans;
	if (K == 0) ans.push_back(f[0][0]);
	REP(i, msum) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	auto used = make_v<bool>(N, N);
	int cnt = 0;
	bool current = 0;
	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum >= (i + j)) {
				q[current].push_back({i, j});
				used[i][j] = true;
			}
		}
	}
	dp.clear();
	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second])
				chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N and not used[p.first][p.second + 1])
				chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second] and f[p.first + 1][p.second] == mc) {
				used[p.first + 1, p.second];
				q[next].push_back({p.first + 1, p.second});
			}
			if (p.second + 1 < N and not used[p.first + 1][p.second] and f[p.first][p.second + 1] == mc) {
				used[p.first, p.second + 1];
				q[next].push_back({p.first, p.second + 1});
			}
		}
		q[current].clear();
		current = not current;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K-1;
	auto dp = make_v<int>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	string ans;
	if (K == 0 and f[0][0] != 'a') ans.push_back(f[0][0]);
	REP(i, msum+1) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	auto used = make_v<bool>(N, N);
	int cnt = 0;
	bool current = 0;
	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum == (i + j)) {
				q[current].push_back({i, j});
				used[i][j] = true;
			}
		}
	}
	dp.clear();
//	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second])
				chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N and not used[p.first][p.second + 1])
				chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second] and f[p.first + 1][p.second] == mc) {
				used[p.first + 1][p.second] = true;
				q[next].push_back({p.first + 1, p.second});
			}
			if (p.second + 1 < N and not used[p.first][p.second + 1] and f[p.first][p.second + 1] == mc) {
				used[p.first][p.second + 1] = true;
				q[next].push_back({p.first, p.second + 1});
			}
		}
		q[current].clear();
		current = not current;
	}
	if (ans[0] == 'a' and ans[ans.size()-1] == 'a')
		ans.pop_back();
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K-1;
	auto dp = make_v<int>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K) {
				f[i][j] = 'a';
			}
		}
	}

	string ans;
	//if (K == 0 and f[0][0] != 'a') ans.push_back(f[0][0]);
	//REP(i, msum+1) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	auto used = make_v<bool>(N, N);
	//int cnt = 0;
	bool current = 0;
	ans.push_back(f[0][0]);
	q[current].push_back({0, 0});
	used[0][0] = true;
/*	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum == (i + j)) {
				q[current].push_back({i, j});
				used[i][j] = true;
			}
		}
	}
*/
	dp.clear();
//	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second])
				chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N and not used[p.first][p.second + 1])
				chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second] and f[p.first + 1][p.second] == mc) {
				used[p.first + 1][p.second] = true;
				q[next].push_back({p.first + 1, p.second});
			}
			if (p.second + 1 < N and not used[p.first][p.second + 1] and f[p.first][p.second + 1] == mc) {
				used[p.first][p.second + 1] = true;
				q[next].push_back({p.first, p.second + 1});
			}
		}
		q[current].clear();
		current = not current;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K-1;
	auto dp = make_v<int>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	string ans;
	if (K == 0 and f[0][0] != 'a') ans.push_back(f[0][0]);
	REP(i, msum+1) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	auto used = make_v<bool>(N, N);
	int cnt = 0;
	bool current = 0;
	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum == (i + j)) {
				q[current].push_back({i, j});
				used[i][j] = true;
			}
		}
	}
	dp.clear();
//	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second])
				chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N and not used[p.first][p.second + 1])
				chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second] and f[p.first + 1][p.second] == mc) {
				used[p.first + 1][p.second] = true;
				q[next].push_back({p.first + 1, p.second});
			}
			if (p.second + 1 < N and not used[p.first][p.second + 1] and f[p.first][p.second + 1] == mc) {
				used[p.first][p.second + 1] = true;
				q[next].push_back({p.first, p.second + 1});
			}
		}
		q[current].clear();
		current = not current;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K-1;
	auto dp = make_v<int>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K) {
				f[i][j] = 'a';
			}
		}
	}

	string ans;
	//if (K == 0 and f[0][0] != 'a') ans.push_back(f[0][0]);
	//REP(i, msum+1) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	auto used = make_v<bool>(N, N);
	//int cnt = 0;
	bool current = 0;
	ans.push_back(f[0][0]);
	q[current].push_back({0, 0});
	used[0][0] = true;
/*	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum == (i + j)) {
				q[current].push_back({i, j});
				used[i][j] = true;
			}
		}
	}
*/
	dp.clear();
//	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second])
				chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N and not used[p.first][p.second + 1])
				chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second] and f[p.first + 1][p.second] == mc) {
				used[p.first + 1][p.second] = true;
				q[next].push_back({p.first + 1, p.second});
			}
			if (p.second + 1 < N and not used[p.first][p.second + 1] and f[p.first][p.second + 1] == mc) {
				used[p.first][p.second + 1] = true;
				q[next].push_back({p.first, p.second + 1});
			}
		}
		q[current].clear();
		current = not current;
	}
	if (ans[0] == 'a' and ans[ans.size()-1] == 'a')
		ans.pop_back();
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K;
	auto dp = make_v<ll>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	string ans;
	if (K == 0) ans.push_back(f[0][0]);
	REP(i, msum) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	int cnt = 0;
	bool current = 0;
	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum >= (i + j)) {
				q[current].push_back({i, j});
			}
		}
	}
	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N) chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N) chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and f[p.first + 1][p.second] == mc)
				q[next].push_back({p.first + 1, p.second});
			if (p.second + 1 < N and f[p.first][p.second + 1] == mc)
				q[next].push_back({p.first, p.second + 1});
		}
		q[current].clear();
		current = not current;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K;
	auto dp = make_v<int>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	string ans;
	if (K == 0) ans.push_back(f[0][0]);
	REP(i, msum) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	int cnt = 0;
	bool current = 0;
	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum >= (i + j)) {
				q[current].push_back({i, j});
			}
		}
	}
	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N) chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N) chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and f[p.first + 1][p.second] == mc)
				q[next].push_back({p.first + 1, p.second});
			if (p.second + 1 < N and f[p.first][p.second + 1] == mc)
				q[next].push_back({p.first, p.second + 1});
		}
		q[current].clear();
		current = not current;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	auto dp = make_v<ll>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	int msum = 0;
	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	char nc = 'z';
	pair<int, int> ni;
	REP(i, N-1) {
		REP(j, N-1) {
			if ((i + j == msum) and dp[i][j] <= K) {
				char c1 = 'z' + 1;
				char c2 = 'z' + 1;
				if (i + 1 < N) c1 = f[i + 1][j];
				if (j + 1 < N) c2 = f[i][j + 1];
				char next = min(c1, c2);
				if (chmin(nc, next)) {
					ni = {i, j};
				}
			}
		}
	}
	string ans;
	REP(i, ni.first + ni.second + 2 - 1) ans.push_back('a');
//	ans.push_back(f[ni.first][ni.second]);

	while (true) {
		char c1 = 'z' + 1;
		char c2 = c1;
		if (ni.first + 1 < N) c1 = f[ni.first + 1][ni.second];
		if (ni.second + 1 < N) c2 = f[ni.first][ni.second + 1];
		char next = min(c1, c2);
		if (next == 'z' + 1) break;
		ans.push_back(next);
		if (next == c1) ni.first++;
		else ni.second++;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	auto dp = make_v<ll>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	int msum = 0;
	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	char nc = 'z' + 1;
	pair<int, int> ni = {0, 0};
	REP(i, N-1) {
		REP(j, N-1) {
			if ((i + j == msum) and dp[i][j] <= K) {
				char c1 = 'z' + 1;
				char c2 = 'z' + 1;
				if (i + 1 < N) c1 = f[i + 1][j];
				if (j + 1 < N) c2 = f[i][j + 1];
				char next = min(c1, c2);
				if (chmin(nc, next)) {
					ni = {i, j};
				}
			}
		}
	}
	string ans;
	if (nc <= 'z')
		REP(i, ni.first + ni.second + 2 - 1) ans.push_back('a');
	else
		ans.push_back(f[0][0]);
//	ans.push_back(f[ni.first][ni.second]);

	while (true) {
		char c1 = 'z' + 1;
		char c2 = c1;
		if (ni.first + 1 < N) c1 = f[ni.first + 1][ni.second];
		if (ni.second + 1 < N) c2 = f[ni.first][ni.second + 1];
		char next = min(c1, c2);
		if (next == 'z' + 1) break;
		ans.push_back(next);
		if (next == c1) ni.first++;
		else ni.second++;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	auto dp = make_v<ll>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	int msum = 0;
	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	string ans;
	REP(i, msum) ans.push_back('a');

	priority_queue<
		pair<char, pair<int, int>>,
		vector<pair<char, pair<int, int>>>,
		greater<>
	> q;
	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and msum == (i + j)) {
				q.push({'a', {i, j}});
			}
		}
	}
	while (true) {
		auto ff = q.top();
		char c = ff.first;
		ans.push_back(c);
		queue<pair<int, int>> next;
		next.push(ff.second);
		while (q.size()) {
			if (c == q.top().first) {
				next.push(q.top().second);
			}
			q.pop();
		}
		while (next.size()) {
			auto p = next.front(); next.pop();
			if (p.first + 1 < N)
				q.push({f[p.first + 1][p.second], {p.first + 1, p.second}});
			if (p.second + 1 < N)
				q.push({f[p.first][p.second + 1], {p.first, p.second + 1}});
		}
		if (q.size() == 0) break;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K-1;
	auto dp = make_v<int>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	string ans;
	if (K == 0) ans.push_back(f[0][0]);
	REP(i, msum+1) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	auto used = make_v<bool>(N, N);
	int cnt = 0;
	bool current = 0;
	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum == (i + j)) {
				q[current].push_back({i, j});
				used[i][j] = true;
			}
		}
	}
	dp.clear();
//	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second])
				chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N and not used[p.first][p.second + 1])
				chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second] and f[p.first + 1][p.second] == mc) {
				used[p.first + 1][p.second] = true;
				q[next].push_back({p.first + 1, p.second});
			}
			if (p.second + 1 < N and not used[p.first][p.second + 1] and f[p.first][p.second + 1] == mc) {
				used[p.first][p.second + 1] = true;
				q[next].push_back({p.first, p.second + 1});
			}
		}
		q[current].clear();
		current = not current;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	auto dp = make_v<ll>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	int msum = 0;
	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	char nc = 'z' + 1;
	pair<int, int> ni = {0, 0};
	REP(i, N-1) {
		REP(j, N-1) {
			if ((i + j == msum) and dp[i][j] <= K) {
				char c1 = 'z' + 1;
				char c2 = 'z' + 1;
				if (i + 1 < N) c1 = f[i + 1][j];
				if (j + 1 < N) c2 = f[i][j + 1];
				char next = min(c1, c2);
				if (chmin(nc, next)) {
					ni = {i, j};
				}
			}
		}
	}
	string ans;
	if (nc <= 'z')
		REP(i, ni.first + ni.second + 2 - 1) ans.push_back('a');
//	ans.push_back(f[ni.first][ni.second]);

	while (true) {
		char c1 = 'z' + 1;
		char c2 = c1;
		if (ni.first + 1 < N) c1 = f[ni.first + 1][ni.second];
		if (ni.second + 1 < N) c2 = f[ni.first][ni.second + 1];
		char next = min(c1, c2);
		if (next == 'z' + 1) break;
		ans.push_back(next);
		if (next == c1) ni.first++;
		else ni.second++;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K;
	auto dp = make_v<int>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	string ans;
	if (K == 0) ans.push_back(f[0][0]);
	REP(i, msum) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	auto used = make_v<bool>(N, N);
	int cnt = 0;
	bool current = 0;
	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum >= (i + j)) {
				q[current].push_back({i, j});
				used[i][j] = true;
			}
		}
	}
	dp.clear();
	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second])
				chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N and not used[p.first][p.second + 1])
				chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second] and f[p.first + 1][p.second] == mc) {
				used[p.first + 1][p.second] = true;
				q[next].push_back({p.first + 1, p.second});
			}
			if (p.second + 1 < N and not used[p.first][p.second + 1] and f[p.first][p.second + 1] == mc) {
				used[p.first][p.second + 1] = true;
				q[next].push_back({p.first, p.second + 1});
			}
		}
		q[current].clear();
		current = not current;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K-1;
	auto dp = make_v<int>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	string ans;
	if (K == 0 and f[0][0] != 'a') ans.push_back(f[0][0]);
	REP(i, msum+((f[N-1][N-1]=='a')?0:1)) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	auto used = make_v<bool>(N, N);
	int cnt = 0;
	bool current = 0;
	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum == (i + j)) {
				q[current].push_back({i, j});
				used[i][j] = true;
			}
		}
	}
	dp.clear();
//	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second])
				chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N and not used[p.first][p.second + 1])
				chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second] and f[p.first + 1][p.second] == mc) {
				used[p.first + 1][p.second] = true;
				q[next].push_back({p.first + 1, p.second});
			}
			if (p.second + 1 < N and not used[p.first][p.second + 1] and f[p.first][p.second + 1] == mc) {
				used[p.first][p.second + 1] = true;
				q[next].push_back({p.first, p.second + 1});
			}
		}
		q[current].clear();
		current = not current;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K;
	auto dp = make_v<int>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	string ans;
	if (K == 0) ans.push_back(f[0][0]);
	REP(i, msum) ans.push_back('a');

	vector<vector<pair<int, int>>> q(2);
	int cnt = 0;
	bool current = 0;
	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K and f[i][j] == 'a') {
				cnt++;
			}
			if (dp[i][j] == K and msum >= (i + j)) {
				q[current].push_back({i, j});
			}
		}
	}
	dp.clear();
	REP(i, cnt) ans.push_back('a');
	if (K == 0) q[current].push_back({0, 0});
	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N) chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N) chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and f[p.first + 1][p.second] == mc)
				q[next].push_back({p.first + 1, p.second});
			if (p.second + 1 < N and f[p.first][p.second + 1] == mc)
				q[next].push_back({p.first, p.second + 1});
		}
		q[current].clear();
		current = not current;
	}
	out(ans);

    return 0;
}


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

using ull = unsigned long long;
using ll = long long;

#define REP(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define pb push_back
#define fill(x, y) memset(x, y, sizeof(x))
#define even(x) x % 2 == 0
#define odd(x) x % 2 != 0
#define all(x) x.begin(), x.end()
#define pcnt __builtin_popcount
#define buli(x) __builtin_popcountll(x)
#define F first
#define S second
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
#define IN1(x) ll x; cin >> x;

// these functions return the position of result of Binary Search.
#define LB(s, t, x) (int) (lower_bound(s, t, x) - s)
#define UB(s, t, x) (int) (upper_bound(s, t, x) - s)

ll qp(ll a, ll b, int mo) { ll ans = 1; do { if (b & 1) ans = 1ll * ans * a % mo; a = 1ll * a * a % mo; } while (b >>= 1); return ans; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll lcm(ll a, ll b) { ll temp = gcd(a, b); return temp ? (a / temp * b) : 0; }
int mDays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
int dx8[] = { 1, -1, 0, 0, 1, 1, -1, -1 }, dy8[] = { 0, 0, -1, 1, -1, 1, -1, 1 };

template <typename T>
vector<T> make_v(size_t a) { return vector<T>(a); }
template <typename T, typename... Ts>
auto make_v(size_t a, size_t b, Ts... ts) { return vector<decltype(make_v<T>(b, ts...))>(a, make_v<T>(b, ts...)); }
template <typename T, typename V>
typename enable_if<is_class<T>::value == 0>::type
fill_v(T &t, const V &v) { t = v; }
template <typename T, typename V>
typename enable_if<is_class<T>::value != 0>::type
fill_v(T &t, const V &v) { for (auto &e : t) fill_v(e, v); }
// auto dp = make_v<int>(4, h, w);
// fill_v(dp, 0);

const ll INF_LL = (1ll << 60);
const int INF_INT = (int)1e9;
const ll MOD_CONST = (ll)(1e9 + 7);

template <typename T>
vector<T> pows(int b, int n) {
    vector<T> ret;
    T x = 1;
    while (ret.size() < n) {
        ret.push_back(x);
        x *= b;
    }
    return ret;
}
// find string "from" in "str", and replace them to "to"
void strReplace(std::string& str, const std::string& from, const std::string& to) {
	std::string::size_type pos = 0;
	while (pos = str.find(from, pos), pos != std::string::npos) {
		str.replace(pos, from.length(), to);
		pos += to.length();
	}
}
template <class T>
bool chmax(T &a, const T &b) { if (a < b) { a = b; return 1; } return 0; }
template <class T>
bool chmin(T &a, const T &b) { if (b < a) { a = b; return 1; } return 0; }

inline tuple<ll, ll> rotate45(tuple<ll, ll> point) { ll x = get<0>(point), y = get<1>(point); return tuple<ll, ll>(x + y, x - y); }
inline bool rangeCheck2D(int nx, int ny, int Width, int Height) { return nx >= 0 and nx < Width and ny >= 0 and ny < Height; }

template <typename T>
vector<T> INPA(ll n) {
	vector<T> x;
	REP(i, n) {
		T tmp; cin >> tmp;
		x.push_back(tmp);
	}
	return move(x);
}
template <typename T>
void out(T o) { cout << o << endl; }
template <typename T>
void out(vector<T> &out) { REP(i, (int)out.size()) { cout << out[i]; if (i == (int)out.size() - 1) cout << endl; else cout << " "; } }
template <typename T>
void out(vector<vector<T>> o) { REP(i, o.size()) out(o[i]); }
void YesNo(bool f) { cout << (f?"Yes":"No") << endl; }
void YESNO(bool f) { cout << (f?"YES":"NO") << endl; }

// don't use " for (auto v : V) "
// use " for (const auto& e : V), for (auto&& e : V) "

// min({1, 2, 0}) = 0
// max({1, 2, 0}) = 2

int main(void)
{
    cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(20);

	IN1(N); IN1(K);

	vector<string> f(N);
	REP(i, N) cin >> f[i];

	int msum = K-1;
	auto dp = make_v<int>(N, N);
	if (f[0][0] != 'a') dp[0][0]++;
	FOR(i, 1, N) {
		dp[0][i] = dp[0][i-1];
		if (f[0][i] != 'a') dp[0][i]++;
	}
	FOR(i, 1, N) {
		dp[i][0] = dp[i-1][0];
		if (f[i][0] != 'a') dp[i][0]++;
	}

	FOR(i, 1, N) {
		FOR(j, 1, N) {
			dp[i][j] = min(dp[i][j-1], dp[i-1][j]);
			if (f[i][j] != 'a')
				dp[i][j]++;
			if (dp[i][j] <= K) {
				chmax(msum, i + j);
			}
		}
	}

	REP(i, N) {
		REP(j, N) {
			if (dp[i][j] <= K) {
				f[i][j] = 'a';
			}
		}
	}

	string ans;
	vector<vector<pair<int, int>>> q(2);
	auto used = make_v<bool>(N, N);
	bool current = 0;

	ans.push_back(f[0][0]);
	q[current].push_back({0, 0});
	used[0][0] = true;

	while (true) {
		char mc = 'z' + 1;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second])
				chmin(mc, f[p.first + 1][p.second]);
			if (p.second + 1 < N and not used[p.first][p.second + 1])
				chmin(mc, f[p.first][p.second + 1]);
		}
		if (mc == 'z' + 1) break;
		ans.push_back(mc);
		bool next = not current;
		REP(i, q[current].size()) {
			auto p = q[current][i];
			if (p.first + 1 < N and not used[p.first + 1][p.second] and f[p.first + 1][p.second] == mc) {
				used[p.first + 1][p.second] = true;
				q[next].push_back({p.first + 1, p.second});
			}
			if (p.second + 1 < N and not used[p.first][p.second + 1] and f[p.first][p.second + 1] == mc) {
				used[p.first][p.second + 1] = true;
				q[next].push_back({p.first, p.second + 1});
			}
		}
		q[current].clear();
		current = not current;
	}
	out(ans);

    return 0;
}


#include <bits/extc++.h>
#define N 2005
using namespace std;

struct node {
	int i, j;
	char c;
	string s;
	node() {}
	node(int i, int j, char c) : i(i), j(j), c(c), s(string(i + j, 'a') + c) {}
	bool operator < (const node &m) const{
		return i + j == m.i + m.j ? c > m.c : i + j < m.i + m.j;
	}
	bool operator == (const node &m) const{
		return !(*this < m) && !(m < *this) && i != m.i;
	}
};
priority_queue<node> pq;
queue<node> q;
char s[N][N];
int n, m, f[N][N];
int main() {
	//freopen("D.in", "r", stdin);
	scanf("%d%d", &n, &m);
	for(int i = 0; i < n; ++i)
		scanf("%s", s[i]);
	memset(f, 0x80, sizeof(f));
	f[0][0] = m;
	for(int k = 0; k < 2 * n - 1; ++k)
		for(int i = min(k, n - 1); i >= 0; --i) {
			int j = k - i;
			if(f[i][j] < 0)
				continue;
			if(!f[i][j]) {
				pq.push(node(i, j, s[i][j]));
				continue;
			}
			if(s[i][j] != 'a')
				--f[i][j], s[i][j] = 'a';
			if(i < n - 1 && f[i + 1][j] < f[i][j])
					f[i + 1][j] = f[i][j];
			if(j < n - 1 && f[i][j + 1] < f[i][j])
					f[i][j + 1] = f[i][j];
		}
	if(f[n - 1][n - 1] >= 0)
		return cout << string(2 * n - 1, 'a') << endl, 0;
	while(true) {
		while(!pq.empty()) {
			if(q.empty() || pq.top() == q.front())
				q.push(pq.top());
			pq.pop();
		}
		if(q.front().i == n - 1 && q.front().j == n - 1) {
			cout << q.front().s << endl;
			break;
		}
		while(!q.empty()) {
			node x = q.front();q.pop();
			if(x.i < n - 1) {
				node y = x;
				++y.i;
				y.s += y.c = s[y.i][y.j];
				pq.push(y);
			}
			if(x.j < n - 1) {
				node y = x;
				++y.j;
				y.s += y.c = s[y.i][y.j];
				pq.push(y);
			}
		}
	}
	return 0;
}


#include <bits/extc++.h>
#define N 2005
using namespace std;

struct node {
	int i, j;
	char c;
	string s;
	node() {}
	node(int i, int j, char c) : i(i), j(j), c(c), s(string(i + j, 'a') + c) {}
	bool operator < (const node &m) const{
		return i + j == m.i + m.j ? c > m.c : i + j < m.i + m.j;
	}
	bool operator == (const node &m) const{
		return !(*this < m) && !(m < *this) && i != m.i;
	}
};
priority_queue<node> pq;
queue<node> q;
char s[N][N];
int n, m, f[N][N];
int main() {
	//freopen("D.in", "r", stdin);
	scanf("%d%d", &n, &m);
	for(int i = 0; i < n; ++i)
		scanf("%s", s[i]);
	memset(f, 0x80, sizeof(f));
	f[0][0] = m;
	for(int k = 0; k < 2 * n - 1; ++k)
		for(int i = min(k, n - 1); i >= max(0, k - n + 1); --i) {
			int j = k - i;
			if(f[i][j] < 0)
				continue;
			if(!f[i][j]) {
				pq.push(node(i, j, s[i][j]));
				continue;
			}
			if(s[i][j] != 'a')
				--f[i][j], s[i][j] = 'a';
			if(i < n - 1 && f[i + 1][j] < f[i][j])
					f[i + 1][j] = f[i][j];
			if(j < n - 1 && f[i][j + 1] < f[i][j])
					f[i][j + 1] = f[i][j];
		}
	if(f[n - 1][n - 1] >= 0)
		return cout << string(2 * n - 1, 'a') << endl, 0;
	while(true) {
		while(!pq.empty()) {
			if(q.empty() || pq.top() == q.front())
				q.push(pq.top());
			pq.pop();
		}
		if(q.front().i == n - 1 && q.front().j == n - 1) {
			cout << q.front().s << endl;
			break;
		}
		while(!q.empty()) {
		node x = q.front();q.pop();
			if(x.i < n - 1) {
				node y = x;
				++y.i;
				y.s += y.c = s[y.i][y.j];
				pq.push(y);
			}
			if(x.j < n - 1) {
				node y = x;
				++y.j;
				y.s += y.c = s[y.i][y.j];
				pq.push(y);
			}
		}
	}
	return 0;
}


#include <bits/extc++.h>
#define N 2005
using namespace std;

struct node {
	int i, j;
	char c;
	bool last;
	node() {}
	node(int i, int j, char c, bool last) : i(i), j(j), c(c), last(last) {}
	bool operator < (const node &m) const{
		return i + j == m.i + m.j ? c > m.c : i + j < m.i + m.j;
	}
	bool operator == (const node &m) const{
		return !(*this < m) && !(m < *this) && i != m.i;
	}
};
priority_queue<node> pq;
node q[N << 2];
int front, back;
char s[N][N];
int n, m, f[N][N];
bool last[N][N];
void Print(int i, int j) {
	if(i || j)
		if(last[i][j])
			Print(i - 1, j);
		else
			Print(i, j - 1);
	putchar(s[i][j]);
}
int main() {
	//freopen("D.in", "r", stdin);
	scanf("%d%d", &n, &m);
	for(int i = 0; i < n; ++i)
		scanf("%s", s[i]);
	memset(f, 0x80, sizeof(f));
	f[0][0] = m;
	for(int k = 0; k < 2 * n - 1; ++k)
		for(int i = min(k, n - 1); i >= max(0, k - n + 1); --i) {
			int j = k - i;
			if(f[i][j] < 0)
				continue;
			if(!f[i][j] && s[i][j] != 'a') {
				pq.push(node(i, j, s[i][j], last[i][j]));
				continue;
			}
			if(s[i][j] != 'a')
				--f[i][j], s[i][j] = 'a';
			if(i < n - 1 && f[i + 1][j] < f[i][j])
					f[i + 1][j] = f[i][j], last[i + 1][j] = true;
			if(j < n - 1 && f[i][j + 1] < f[i][j])
					f[i][j + 1] = f[i][j], last[i][j + 1] = false;
		}
	if(f[n - 1][n - 1] >= 0)
		return cout << string(2 * n - 1, 'a') << endl, 0;
	while(true) {
		while(!pq.empty()) {
			if(front == back || pq.top() == q[front])
				q[back++] = pq.top(), back = back == N << 2 ? 0 : back;
			pq.pop();
		}
		if(q[front].i == n - 1 && q[front].j == n - 1) {
			last[n - 1][n - 1] = q[front].last;
			Print(n - 1, n - 1);
			puts("");
			break;
		}
		while(front < back) {
			node x = q[front++];front = front == N << 2 ? 0 : front;
			last[x.i][x.j] = x.last;
			if(x.i < n - 1) {
				node y = x;
				++y.i;
				y.last = true;
				y.c = s[y.i][y.j];
				pq.push(y);
			}
			if(x.j < n - 1) {
				node y = x;
				++y.j;
				y.last = false;
				y.c = s[y.i][y.j];
				pq.push(y);
			}
		}
	}
	return 0;
}