Problem:
Score : 1000 points

Problem Statement

You are given a string S consisting of digits between 1 and 9, inclusive.
You will insert at most K commas (,) into this string to separate it into multiple numbers.

Your task is to minimize the maximum number among those produced by inserting commas. Find minimum possible such value.

Constraints

0 ≦ K < |S| ≦ 100,000

S consists of digits between 1 and 9, inclusive.

Partial Scores

In the test set worth 100 points, |S| ≦ 2.

In the test set worth another 100 points, |S| ≦ 16.

In the test set worth another 200 points, |S| ≦ 100.

In the test set worth another 200 points, |S| ≦ 2,000.

Input

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

K
S

Output

Print the minimum possible value.

Sample Input 1

2
15267315

Sample Output 1

315

When the string is separated into 152, 67 and 315, the maximum among these is 315, which is the minimum possible value.

Sample Input 2

0
12456174517653111

Sample Output 2

12456174517653111

12456174517653111 itself is the answer.

Sample Input 3

8
127356176351764127645176543176531763517635176531278461856198765816581726586715987216581

Sample Output 3

5317635176

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

#include <iostream>
#include <set>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
#include <string>
#include <cstring>
#include <climits>
#include <sstream>
#include <iomanip>
#include <map>
#include <stack>
#include <numeric>

using namespace std;

/*-----------------------------------------------------------------------------
　定義
 -----------------------------------------------------------------------------*/
#define ALL(x)					(x).begin(),(x).end()
#define REP(i, n)				for (int (i) = 0 ; (i) < (ll)(n) ; ++(i))
#define REPN(i, m, n)			for (int (i) = m ; (i) < (ll)(n) ; ++(i))
#define INF						(int)2e9
#define MOD						(1000 * 1000 * 1000 + 7)
#define Ceil(x, n)				(((((x))+((n)-1))/n))		/* Nの倍数に切り上げ割り算 */
#define CeilN(x, n)				(((((x))+((n)-1))/n)*n)		/* Nの倍数に切り上げ */
#define FloorN(x, n)			((x)-(x)%(n))				/* Nの倍数に切り下げ */
#define IsOdd(x)				(((x)&0x01UL) == 0x01UL)			
#define IsEven(x)				(!IsOdd((x)))						
#define M_PI					3.14159265358979323846
typedef long long				ll;
typedef unsigned long			ul;
typedef unsigned long long		ull;
typedef pair<int, int>			P;

/*-----------------------------------------------------------------------------
　処理
 -----------------------------------------------------------------------------*/
class SuffixArray {

public:
	int m_strLen;
	vector<int> sa;			// 順番 → インデックス
	vector<int> idxToOrder;	// インデックス → 順番

private:
	string srcStr;
	vector<int> rank;
	int m_i;

    bool lb_substr(const string &pat, int si = 0, int ti = 0) 
	{
		int sn = m_strLen;
		int tn = pat.size();
		while (si < sn && ti < tn) {
			if (srcStr[si] < pat[ti]) return true;
			if (srcStr[si] > pat[ti]) return false;
			si++, ti++;
		}
		return (si >= sn) && (ti < tn);
	}

public:
	SuffixArray(const string &s)
	{
		srcStr = s;
		m_strLen = s.size();

         /// @param i 元の文字列Sの位置i S[i:]
         /// @param j 元の文字列Sの位置j S[j:]
		auto compare_sa = [&](int a, int b)	{
			int rnkA = rank[a];
			int rnkB = rank[b];
			if (rnkA != rnkB) {
				return (rnkA < rnkB);
			} else {
				int aIdx = a + m_i;
				int bIdx = b + m_i;
				int ra = (aIdx <= m_strLen) ? rank[aIdx] : -1;
				int rb = (bIdx <= m_strLen) ? rank[bIdx] : -1;
				return ra < rb;
			}
		};

		// 最初は1文字
		sa.resize(m_strLen + 1);
		for (int i = 0; i <= m_strLen; i++) {
			sa[i] = i;
		}

		// ランクは文字コード
		rank.resize(m_strLen + 1);
		for (int i = 0; i < m_strLen; i++) {
			rank[i] = s[i];
		}
		rank[m_strLen] = -1;	// 空文字
	
		// i文字についてソートされてるところから2*i文字でソートする
		for (m_i = 1; m_i <= m_strLen; m_i *= 2) {
			sort(sa.begin(), sa.end(), compare_sa);
			
			auto nextRank = rank;
			nextRank[sa[0]] = 0;
			for (int j = 1; j <= m_strLen; j++) {
				bool bCmpRet = compare_sa(sa[j - 1], sa[j]);
				nextRank[sa[j]] = nextRank[sa[j - 1]];
				if (compare_sa(sa[j - 1], sa[j])) {
					nextRank[sa[j]]++;
				}
				// nextRank[sa[j]] = nextRank[sa[j - 1]] + bCmpRet ? 1 : 0;
			}
			rank = nextRank;
		}

		// 逆引き用
		idxToOrder.resize(m_strLen + 1);
		for (int i = 0; i <= m_strLen; i++) {
			idxToOrder[sa[i]] = i;
		}
	}

	// 文字列検索
	bool contain(const string &contStr)
	{
		int contStrLen = contStr.length();
		int lb = 0;
		int lu = m_strLen + 1;
		int mid = 0;
		while ((lb + 1) != lu) {
			mid = (lb + lu) / 2;
			if (srcStr.compare(sa[mid], contStrLen, contStr) < 0) {
				lb = mid;
			} else {
				lu = mid;
			}
		}
		return (srcStr.compare(sa[lu], contStrLen, contStr) == 0);
	}

	/// @param pat 文字列パターン
	/// @details 1 ~ n 正常(0は空文字)
	/// @details n + 1 .end()
	/// @details SA[high:]が下限
	int lower_bound(const string &pat) 
	{
		int low = 0;
		int high = m_strLen + 1;
		while (high - low > 1) {
			int mid = (low + high) / 2;
			if (lb_substr(pat, sa[mid])) low = mid;
			else high = mid;
		}
		return high;
	}
 
	/// @param pat 文字列パターン
	/// @details 1 ~ n 正常(0は空文字)
	/// @details n + 1 .end()
	/// @details SA[:high)が上限
	int upper_bound(string &pat) 
	{
		int low = 0;
		int high = m_strLen + 1;
		pat.back()++;
		while (high - low > 1) {
			int mid = (low + high) / 2;
			if (lb_substr(pat, sa[mid])) low = mid;
			else high = mid;
		}
		pat.back()--;
		return high;
	}

	void dumpSaStr()
	{
		int idx = 0;
		for (auto strIdx : sa) {
			cout<<  idx++ << " " << strIdx << " " << srcStr.substr(strIdx) << endl;
		}
	}
};

bool compExec(SuffixArray &sa, int orderMax, int K, int subStrLen)
{
    /*int i = 0, res = 0;
    while (i < sa.m_strLen){
        if(sa.idxToOrder[i] <= orderMax){
            i += subStrLen;
        }else{
            i += subStrLen - 1;
        }
        res++;
        if(res > K) return false;
    }
    return (res <= K);*/

	if (subStrLen == 1) {
		for (int strPos = 0; strPos < sa.m_strLen; strPos++) {
			if (orderMax < sa.idxToOrder[strPos]) {
				return false;
			}
		}
		return true;
	} else {
		int strCnt = 0;
		for (int strPos = 0; strPos < sa.m_strLen; ) {
			strCnt++;
			if ((strPos + subStrLen) <= sa.m_strLen) {
				if (sa.idxToOrder[strPos] <= orderMax) {
					strPos += subStrLen;
				} else {
					strPos += (subStrLen - 1);
				}
			} else {
				break;
			}
		}
		return (strCnt <= K);
	}
}

int main()
{
	int K;
	string S;
	cin >> K >> S;
	if (K == 0) {
		cout << S << endl;	
		return 0;
	}

	// 文字列長と探す個数
	K++;
	int subStrLen = Ceil(S.size(), K);
	
	// SA計算
	SuffixArray SA(S);
	// SA.dumpSaStr();

	// 数字の候補
	vector<P> kouho;
	REP(i, (S.size() - subStrLen + 1)) {
		kouho.emplace_back(make_pair(SA.idxToOrder[i], i));
	}
	sort(ALL(kouho));

	// 候補を2分探索
	int lb = 0;
	int lu = kouho.size() + 1;
	int mid;
	while (lb + 1 != lu) {
		mid = (lb + lu) / 2;
		int midIdx = kouho[mid].second;
		int orderMax = SA.idxToOrder[midIdx];
		if (!compExec(SA, orderMax, K, subStrLen)) {
			lb = mid;
		} else {
			lu = mid;
		}
	}

	while(SA.m_strLen < (kouho[lu].second + subStrLen)) {
		lu++;
	}
	cout << S.substr(kouho[lu].second, subStrLen) << endl;	
	return 0;
}