题意:
n个字符串,a-z自定义映射为0-25,每个字符串看做一个26进制数,在限制条件下,求n个数和的最大值。
思路:
1、存下每一种字母(26种,容量为26的结构体数组)在每一位出现的次数(100000位,结构体内容量为100000的int数组),方便比较每一位都要符合26进制,然后从低到高进行排序(贪心);
2、但是要注意,题目虽然说一定有一个字母没有出现在最高位过,保证了一定存在合法的映射,(合法即不能出现前导零),但是排序后的序列可能将出现在最高位过的字母排到最后一位,赋值为0就非法了。所以这种情况需要特判,然后从后往前找第一个没出现在最高位的字母替换到最后,前面依次迁移
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define N 100005
#define M 105
#define INF 0x3f3f3f3f
#define mod 1000000007
char s[N];
LL all[N]; // all[i] 表示 26的i次方对 mod 取膜 后剩下的数
bool has[26];
struct node
{
int index;
int num[N]; //表示每一位的数字
bool friend operator <(node x,node y){
for(int i=N-1;i>=0;i--){
if(x.num[i]!=y.num[i]) return x.num[i] < y.num[i];
}
return 0;
}
}no[26];
int main()
{
all[0] = 1;
for(int i=1;i<N;i++) all[i] = all[i-1] * 26 % mod; //预处理 26
int n;
int cas = 1;
while(~scanf("%d",&n)){
memset(has,0,sizeof(has));
for(int i=0;i<26;i++){
memset(no[i].num,0,sizeof(no[i].num));
no[i].index = i;
}
while(n--){
scanf("%s",s);
has[s[0]-'a'] = 1; //标记,在首位出现过
int len = strlen(s);
for(int i=0;i<len;i++){
no[s[i]-'a'].num[len-1-i]++;
}
}
int zero;
for(int i=0;i<26;i++){ //进位,便于比较
int yu = 0;
for(int j=0;j<N;j++){
int w = yu;
yu = (yu + no[i].num[j]) / 26;
no[i].num[j] = (no[i].num[j] + w) % 26;
}
}
sort(no,no+26);
for(int i=0;i<26;i++){
if(!has[no[i].index]){ //找最小的,未在首位出现的数,置为0
zero = i;
break;
}
}
LL ans = 0;
for(int i=25;i>=0;i--){
LL No;
if(i==zero) continue; //为0,跳过
else if(i > zero) No = i;
else No = i + 1; //在为0的数后面,则进一位
for(int j=0;j<N;j++){
if(!no[i].num[j]) continue;
ans = (ans + all[j] * no[i].num[j] * No) % mod;
}
}
printf("Case #%d: %lld\n",cas++,ans);
}
}