1493 C. K-beautiful Strings(从前往后贪心)

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 );
	} 
}

你可能感兴趣的:(div题解,神仙思维题)