問題:
これは問題の難しいバージョンです。唯一の違いは、n と k の制約です。すべてのバージョンの問題が解決された場合にのみハッキングを行うことができます。
文字列 s があり、それに対して 2 種類の操作を実行できます。
Delete the last character of the string.
Duplicate the string: s:=s+s
、ここで +
denotes concatenation.
各操作は何度でも使用できます (まったく使用しないこともできます)。
あなたの仕事は、文字列 s に対してこれらの操作を実行することによって取得できる長さちょうど k の辞書編集上最小の文字列を見つけることです。
文字列 a は文字列 b よりも辞書編集的に小さい
次のいずれかが当てはまる場合に限ります: a は b の接頭辞ですが、a≠b;
a と b が異なる最初の位置には
、文字列 a のアルファベットで b の対応する文字よりも先に現れる文字が含まれています。
入力
最初の行には、2 つの整数 n、k (1≤n,k≤5⋅105) が含まれます。これは、元の文字列の長さと目的の文字列の長さを足したものです。
2 行目には、n 個の小文字の英字で構成される文字列 s が含まれています。
出力文字列 s に対して演算を実行することで取得できる
長さ k の辞書編集上の最小の文字列を出力します。
例
入力
8 16
dbcadabc
出力
dbcadabcdbcadabc
入力
4 5
abcd
出力
aaaaa
アイデア:
まず 2 番目の文字からトラバースを開始し、最初の文字と比較し、文字が大きい場合は直接マークしてから分割し、等しい場合は 2 つの同一の文字から開始し、不等号が見つかるまで逆方向に比較してから、分類して処理します。詳細についてはコードを参照してください。
コード:
#include <bits/stdc++.h>
#define DEBUG freopen("_in.txt", "r", stdin);
// #define DEBUG freopen("_in.txt", "r", stdin),freopen("_out.txt", "w", stdout);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll maxn = 5e5 + 10;
const ll maxm = maxn * maxn;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 2147493647;
ll t, n, m, k;
char str[maxn], str1[maxn];
int main()
{
// DEBUG;
scanf("%lld%lld", &n, &k);
scanf("%s", str + 1);
ll pos=0;
ll i=2;
while (i<=n)
{
if (str[i] > str[1])
{
pos = i-1;
break;
}
else if (str[i] == str[1])
{
ll j = 1, num = i;
while (i<=n&& j < num&&str[i] == str[j] )
{
i++;//这里i比j小了,就一定比第一个小,保证O(n)
j++;
}
if(i>n)
{
pos = num-1;
break;
}
if (j == num)
continue;
if(str[i]<str[j]) continue;
pos = num-1;
break;
}
else
{
i++;
}
}
if(pos==0)
{
pos=n;
}
for (ll i = 1; i <= k; i++)
{
if (i % pos != 0)
printf("%c", str[i % pos]);
else
printf("%c", str[pos]);
}
printf("\n");
return 0;
}