题意:n串字母,总长度不超过1e6,要求a-z向0-25映射,使字符串对应得到的26进制的数字之和最大。
思路:先计算每个字母在所有串中的贡献,然后通过sort排序贡献和从大到小,然后确定相应的权值,如果所有字母都存在且贡献最小的不能为0即位于某个字符串首位,则寻找第一个能为0的最小的字母将其权值置为0,然后将这些位的权值都向前移一位。
代码:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <cstdio>
#define LL long long
using namespace std;
const LL mod = 1e9+7;
const int maxn = 1e5+15;
const int N = 1e5;
struct node
{
int val[maxn], key, maxlen;
bool operator<(const node &k) const
{
int len = max(maxlen, k.maxlen)+5;
for(int i = len; i >= 0; --i)
{
if(val[i] == k.val[i]) continue;
return val[i] > k.val[i];
}
}
} lt[30];
int n, real[maxn], mark[30];
LL w[maxn], ans;
char s[maxn];
inline void init()
{
w[0] = 1;
for(int i = 1; i <= N; ++i) w[i] = w[i-1]*26%mod;
}
int main()
{
int count = 0; init();
while(~scanf("%d", &n))
{
memset(mark, 0, sizeof mark);
for(int i = 0; i < 26; ++i)
{
memset(lt[i].val, 0, sizeof lt[i].val);
lt[i].key = i;
lt[i].maxlen = 0;
}
for(int i = 1; i <= n; ++i)
{
scanf("%s", s);
int len = strlen(s);
mark[s[0]-'a'] = 1;
for(int j = 0; j < len; ++j)
{
++lt[s[j]-'a'].val[len-j-1];
lt[s[j]-'a'].maxlen = max(lt[s[j]-'a'].maxlen, len-j-1);
}
}
for(int i = 0; i < 26; ++i)
for(int j = 0; j <= lt[i].maxlen+5; ++j)
//极端数据100000个长度为10字符串,进位会超出暂时保存的maxlen,多+5即可,因为100000至多除26为3次
{
lt[i].val[j+1] += lt[i].val[j]/26;
lt[i].val[j] %= 26;
}
sort(lt, lt+26);
for(int i = 25; i >= 0; --i)
{
if(i == 25 && mark[lt[i].key])
{
int t = i; --i;
while(mark[lt[i].key])
{
real[lt[i].key] = 25-i+1;
--i;
}
real[lt[i].key] = 0;
real[lt[t].key] = 1;
}
else
real[lt[i].key] = 25-i;
}
ans = 0;
for(int i = 0; i < 26; ++i)
{
for(int j = 0; j <= lt[i].maxlen+5; ++j)
ans = (ans+real[lt[i].key]*w[j]%mod*lt[i].val[j]%mod)%mod;
}
printf("Case #%d: %lld\n", ++count, ans);
}
return 0;
}
继续加油~