题目链接:
题目大意:
给出一个字符串,问能否通过打乱它的顺序得到一个新的字符串,这个字符串满足所有的质数位和位数是这个质数的倍数的位数上的字母相同,输出能否,如果能,构造一组解.
题目分析:
- 首先我们知道输入的字符串的字母的顺序无所谓,我们只需要记录每个字母出现的次数。
- 然后我们发现不管n是多少,集合的形式一定是一个大集合,和一些规模为1的小集合,且这些集合的元素是质数,且大集合不存在这个质数的倍数。这个很显然。
- 利用上面的结论,我们只需要找到大集合的规模,然后看是否存在一个字母的数量不小于这个大集合的规模,如果存在,那么显然有解。否则无解。
- 构造最终的解,只需要用找出的字母构造大集合中每个位置的字母,其他的随意即可。
AC代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define MAX 1007
using namespace std;
char s[MAX];
int maxPrime[MAX];
int mark[MAX];
int num[30];
void init ()
{
memset ( maxPrime , -1 , sizeof ( maxPrime ) );
for ( int i = 2 ; i < MAX ; i++ )
{
if (~maxPrime[i]) continue;
for ( int j = i*2 ; j < MAX ; j += i )
maxPrime[j] = i;
}
}
int main ( )
{
init ();
while (~scanf ( "%s" , s ) )
{
int n = strlen ( s );
memset ( mark , 0 , sizeof ( mark ) );
if ( n < 4 )
{
puts ( "YES" );
puts ( s );
continue;
}
int cnt = 2;
for ( int i = n ; i >= 2; i-- )
{
if ( mark[i] && maxPrime[i] == -1 ) continue;
if ( ~maxPrime[i] )
{
mark[i] = true;
for ( int j = 2 ; j*j <= i ; j++ )
{
if ( i%j ) continue;
mark[j] = mark[i/j] = true;
}
}
else cnt++;
}
memset ( num , 0 , sizeof ( num ));
for ( int i = 0 ; i < n ; i++ )
num[s[i]-97]++;
int id = -1;
for ( int i = 0 ; i < 26 ; i++ )
if ( n-cnt+1 <= num[i] )
{
id = i;
num[i] -= (n-cnt+1);
break;
}
if ( id == -1 )
{
puts ( "NO" );
continue;
}
for ( int i = 0 ; i < n ; i++ )
{
if ( mark[i+1] ) s[i] =(char)(id+97);
else
for ( int j = 0 ; j < 26 ; j++ )
if ( num[j] )
{
num[j]--;
s[i] = (char)(j+97);
break;
}
}
puts ( "YES");
puts ( s );
}
}