当时比赛这个题,wa了次,最后发现思路是有一点问题。今天补题,也是出现了很多错误,哎,还是太菜了,必须要多写代码。
题意:将a-z这26个字母用0-25进行赋值。那么对于一个字符串,就可以看作是一个26进制,那么该字符串就可以转化为一个10进制的数,求所有字符串的和的最大值。(如果一个字母被赋值为0,那么该字母不能出现在首位。a这种是可以的)
比赛时候的思路是:我把每一个位赋初值0,100000,200000....这种。每个字母出现在每一位的时候就++,然后把所有字母的和相加,可以对这些字母进行排序。
后来想的是通过用num[26][100010],表示该字母在这一位上的个数,如果数量超过26,就进位。
代码如下:
//高精度的思想求每个字母的权值
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll num[28][100050];
ll high[27],alph[27],lead[27],weight[27];//high记录每个字母的最高位是第几位,alph记录字母的权值大小,从大到小,lead记录前导零,weight记录权值
bool cmp(ll a,ll b)
{
if(high[a]==high[b])
{
for(ll j=high[a];j>=0;j--)
{
if(num[a][j]!=num[b][j])
return num[a][j]>num[b][j];
}
}
else
return high[a]>high[b];
}
long long pow(long long a,long long x)//以防万一,这里以后都要有long long
{
long long res=1;
while(x)
{
if(x&1)
res=(res*a)%mod;
a=(a*a)%mod;
x>>=1;
}
return res%mod;
}
int main()
{
ll n,kase=1;
while(~scanf("%lld",&n))
{
char s[100010];
ll lenn=-1;
memset(num,0,sizeof(num));
memset(lead,0,sizeof(lead));
memset(high,0,sizeof(high));
for(int i=0;i<n;i++)
{
scanf("%s",&s);
ll len=strlen(s);
if(len!=1)
lead[s[0]-'a']=1;
for(ll j=len-1;j>=0;j--)
{
num[s[j]-'a'][len-j]++;
if(num[s[j]-'a'][len-j]>=26)//1是最低位
{
num[s[j]-'a'][len-j+1]+=num[s[j]-'a'][len-j]/26;
num[s[j]-'a'][len-j]=num[s[j]-'a'][len-j]%26;
}
}
lenn=max(lenn,len);
}
for(ll i=0;i<26;i++)
{
for(ll j=1;j<lenn+10;j++)
{
if(num[i][j])
high[i]=j;//记录每个字母的最高位
num[i][j+1]+=num[i][j]/26;
num[i][j]=num[i][j]%26;
}
}
for(ll i=0;i<26;i++)
alph[i]=i;
sort(alph,alph+26,cmp);
int k=25;
for(int i=0;i<26;i++)
weight[alph[i]]=k--;
if(weight[alph[25]]==0&&lead[alph[25]])//如果权值最小的那个字母是首字母
{
for(int i=24;i>=0;i--)
{
if(lead[alph[i]]==0)
{
for(int j=25;j>=i;j--)
{
weight[alph[j]]=weight[alph[j-1]];
}
weight[alph[i]]=0;
break;
}
}
}
long long ans=0;
for(ll i=0;i<26;i++)
{
for(ll j=1;j<lenn+26;j++)
{
ans+=((long long)(pow(26,j-1)*weight[i])%mod*num[i][j])%mod;
ans=ans%mod;
}
}
printf("Case #%lld: %lld\n",kase++,ans);
}
}