LINK
另一种解法,官方题解解法
贪心,我们每次都是分配给某个字母 k k k次
所以定义 s u m sum sum表示当前还有多少次没有分配,初始 s u m = n sum=n sum=n
定义 b [ i ] b[i] b[i]表示字母 i i i还剩 b [ i ] b[i] b[i]次没有输出(之前分配了但没有输出)
定义 f l a g flag flag表示当前构造的字典序是否大于原串
那么我们逐个逐个字母去构造
若当前 f l a g = 0 flag=0 flag=0
这表示字典序已经比较大了,接下来就把 b [ 0 ] + = s u m b[0]+=sum b[0]+=sum
然后按照 b b b数组,从字母 ′ a ′ 'a' ′a′一直输出到字母 ′ z ′ 'z' ′z′即可
若当前 f l a g = 1 flag=1 flag=1
这表示字典序和原串是相等的
于是对于当前位,我们从原串的第 i i i个字母循环到 ′ z ′ 'z' ′z′,贪心判断是否能放置这个字母
如何保证放完这个字母后续还能保证字典序是大于等于原串的呢??
Ⅰ如果这一位要放置的字母大于原串字母,肯定放,因为这一位字典序就更大了
Ⅱ如果这一位要放置的字母和原串相等,我们就稍微判断一下
假如把 b [ 25 ] + = s u m b[25]+=sum b[25]+=sum(含义是后续所有字母都分配给 ′ z ′ 'z' ′z′)
然后从 [ i + 1 , n ] [i+1,n] [i+1,n]开始,我们一直往最大的字典序构造看是否能大于等于原串
就是形如 z z z z y y y y y . . . . a a a zzzzyyyyy....aaa zzzzyyyyy....aaa这样放
假如能,那么这一位放当前字母肯定是有解的,我们贪心的放这个字母。
如何判断 z z z z y y y y y . . . . a a a zzzzyyyyy....aaa zzzzyyyyy....aaa的字典序是否大于原串?? 暴力匹配的话,匹配复杂度 O ( n ) O(n) O(n)
稍微优化一下,我们定义 l a s [ i ] [ j ] las[i][j] las[i][j]表示第 i i i个字母往后延续的 j j j字母有多少个,匹配的时候相同字母可以直接跳跃
期望复杂度 O ( 26 n ) O(26n) O(26n),最坏复杂度不知道,但是这样的数据很难构造
#include
using namespace std;
const int maxn = 3e5+10;
char a[maxn];
int n,k,b[30],c[30],las[maxn][27],top,d[30];
char ans[maxn];
bool isok(int nxt,int c[])
{
while( nxt<=n )
{
for(int i='z';i>='a';i--)
{
if( c[i-'a']==0 ) continue;
if( i>a[nxt] ) return true;//已经比较大了
else if( i<a[nxt] ) return false;
else if( c[i-'a']>=las[nxt][i-'a'] ) {
c[i-'a'] -= las[nxt][i-'a'], nxt += las[nxt][i-'a']; break; }
else if( c[i-'a']<las[nxt][i-'a'] ) return false;
}
}
return true;//一直相等,也是合法的
}
int main()
{
int t; cin >> t;
while( t-- )
{
cin >> n >> k >> ( a+1 );
if( n%k!=0 ) {
cout << -1 << endl; continue;}
for(int i=n;i>=1;i--)
for(int j='a';j<='z';j++)
{
if( a[i]==j ) las[i][j-'a'] = las[i+1][j-'a']+1;
else las[i][j-'a'] = 0;
}
int flag = 1,sum = n;
for(int i=1;i<=n;i++)
{
if( flag )
{
for(char j=a[i];j<='z';j++)
{
if( b[j-'a'] )
{
memcpy( c,b,sizeof c );
c[j-'a']--; c['z'-'a'] += sum;
if( j>a[i] || isok(i+1,c) )
{
ans[i] = j; b[j-'a']--;
if( j>a[i] ) flag = 0;
break;
}
}
else
{
if( sum<k ) continue;
memcpy( c,b,sizeof c );
c[j-'a'] += k-1; c['z'-'a'] += sum-k;
if( j>a[i] || isok(i+1,c) )
{
ans[i] = j; sum -= k; b[j-'a'] = k-1;
if( j>a[i] ) flag = 0;
break;
}
}
}
}
else//字典序已经比较大了,后面怎样都无所谓,那么从'a'开始一直往后面放
{
b[0] += sum; int id = i;
memset( d,0,sizeof d );
for(char j='a';j<='z';j++) while( b[j-'a'] ) ans[id++]=j, b[j-'a']--;
break;
}
}
for(int i=1;i<=n;i++) cout << ans[i];
cout << endl;
for(int i=1;i<=n;i++) memset( las[i],0,sizeof las[i] );
memset( b,0,sizeof b );
}
}