http://acm.hdu.edu.cn/showproblem.php?pid=6034
给定一系列字符串,
给每个字母对应的 0-25映射,要求每个串映射的26进制数最大。每个长度大于1的串开头的字母不能映射为0!
思路:先计算每个字母对应的 权值和(比如a出现在第一个串个数位1次,第二个串十数位1次,权值和就是1+26=27)
但是要注意的一点,直接根据权值和从大到小赋值 25-0,是不可以的。
首先是 有 0赋值的限制条件(0映射的字母不能放在第一个串)。
然后是 权值和 在后来会mod,所以直接比较时不可以的。。
所以还要存一个26进制的数组来模拟比较大小。。
题解的排序方法是很好玩的哈哈
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1000006;
int num[30][maxn];//记录每种字母出现的对应26进制。
bool ban[30];//禁止的数。
int sum[30];//每种元素出现的权值和。
char str[maxn];
int a[maxn];//学习一个,用于排序的。
const int mod=1000000007;
ll wq[maxn];
int l;
bool cmp(int A , int B) {
for (int i = l -1; i >= 0 ; -- i) {
if (num[A][i] != num[B][i]) {
return num[A][i] > num[B][i];
}
}
return 0;
}
void init()
{
wq[0]=1;
for(int i=1;i<maxn;i++){
wq[i]=(wq[i-1]*26)%mod;
//求位权
}
}
int w,tt=1;
void solve()
{
l=0;
memset(num,0,sizeof(num));
memset(sum,0,sizeof(sum));
memset(ban,false,sizeof(ban));
for(int i=0;i<w;i++){
scanf("%s",str);
int len=strlen(str);
if(len>1)
ban[str[0]-'a']=true;
reverse(str,str+len);
for(int j=0;j<len;j++){
sum[str[j]-'a']+=wq[j];
while(sum[str[j]-'a']>mod)
sum[str[j]-'a']-=mod;
num[str[j]-'a'][j]++;
}
l=max(l,len);
}
for (int i = 0 ; i < 26 ; ++ i) {
for (int j = 0 ; j < l ; ++ j) {
num[i][j + 1] += num[i][j] / 26;
num[i][j] %= 26;
}
while (num[i][l]) {//可能l会变大,这样
num[i][l + 1] += num[i][l] / 26;
num[i][l ++] %= 26;
}
a[i] = i;
}
sort(a,a+26,cmp);
long long ans=0;
//弄一弄第一个0;
int zero=-1;
for(int i=25;i>=0;i--){
if(!ban[a[i]])
{zero=a[i];break;}
}
int x=25;
for(int i=0;i<=25;i++){
if(a[i]!=zero)
ans=(ans+(sum[a[i]]*1ll*(x--)))%mod;
}
printf("Case #%d: %lld\n",tt++,ans);
}
int main(){
init();
while(~scanf("%d",&w))
solve();
return 0;
}