【题目来源】:http://acm.hdu.edu.cn/showproblem.php?pid=6034
【题意】
26个英文字母,要求用0~25的数字给每一个英文字母赋值,把每一个字符串变成一个个数字,然后,求和。有以下几点,这些构成的数字是26进制,还有,为了求最大的和,必须合理分配每一个数字代表哪个字母,并且,这些数字不会有前导0,也就是说,0这个数字不能够赋值给任意字符串的第一个字母。
【思路】
解释一下样例1:只有一个字符串,一个字母,赋值为25,最大和值是25。
样例2:两个字符串,a在第二位(相当于十位)上有一个,在第一位(相当于个位)上有一个,记成:1 1
b同理,位1 1,那么这两个的谁赋最大值25都一样,得:1*25+1*25*26,1*24+1*24*26,之和便是答案。(至于为啥乘26,想一下十进制,,,每次都乘以10)
样例3:
字母a在第一位上有2个,在第三位上有一个,记为:2 0 1
字母b在第二位上有两个,记为:0 2 0
字母c在第一位上有一个,记为:1 0 0
那么现在进行比较,给哪个赋哪个比较合适,因为各自的最终结果格式都是:第一位数字赋值+第二位数字赋值26+第三位数字赋值*26*26。
思考过后,会发现给a赋值25,b赋值为24,c赋值为23较为合理。
我的大致思路基本上已经在样例里说清楚了,总的来说,就是用数组记下每种字母的位置的个数,然后利用自定义cmp函数进行比较,贪心。
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
typedef long long LL;
char s[100000+10];
int len;
struct pp
{
bool index;//判断是否在第一位
bool ervis;//为了标记是不是要赋值为0
bool vis;//标记是否出现过这种字母
int a[100000+10];
} t[26+10];
bool cmp(pp u,pp v)
{
for(int i=len-1; i>=0; i--)//从高位到低位,逐次比较
{
if(u.a[i]>v.a[i])
return 1;
else if(u.a[i]<v.a[i])
return 0;
}
return 0;
}
int main()
{
int n,cases=1;
while(~scanf("%d",&n))
{
getchar();
memset(t,0,sizeof(t));
len=0;
for(int i=1; i<=n; i++)
{
scanf("%s",s);
int les=strlen(s);
t[s[0]-'a'].index=1;
for(int j=les-1; j>=0; j--)
{
int x=s[j]-'a';
if(t[x].vis!=1) t[x].vis=1;
t[x].a[les-j-1]++;
}
len=max(les,len);//为了使长度一致,选最大的
}
for(int i=0; i<26; i++)
{
for(int j=0; j<len-1; j++)
{
if(t[i].a[j]>26)
{
t[i].a[j+1]+=t[i].a[j]/26;//为了防止出现100 0 和0 1这种情况,先进行字母数量的26进制进位
t[i].a[j]%=26;
}
}
}
sort(t,t+26,cmp);
bool flag=0;//为了判断是否需要给字母赋上0
for(int i=25; i>=0; i--)
{
if(!t[i].vis)
{
flag=1;
break;
}
}
if(!flag)//需要的话,就从后往前找没有出现在字符串首位的字母
{
for(int i=25; i>=0; i--)
{
if(!t[i].index)
{
t[i].ervis=1;
break;
}
}
}
printf("Case #%d: ",cases++);
LL sum=0;
int res=25;
for(int i=0; i<26; i++)
{
if(t[i].vis&&!t[i].ervis)
{
LL tmp=1;
for(int j=0; j<len; j++)
{
if(t[i].a[j])
sum=(sum+tmp*t[i].a[j]*res)%mod;
tmp=(tmp*26)%mod;
}
res--;
}
}
printf("%lld\n",sum);
}
}