2017ACM暑期多校联合训练 - Team 1 1002 HDU 6034 Balala Power! (字符串处理)

题目链接

Problem Description

2017ACM暑期多校联合训练 - Team 1  1002   HDU   6034    Balala Power!   (字符串处理)

Talented Mr.Tang has n strings consisting of only lower case characters. He wants to charge them with Balala Power (he could change each character ranged from a to z into each number ranged from 0 to 25, but each two different characters should not be changed into the same number) so that he could calculate the sum of these strings as integers in base 26 hilariously.

Mr.Tang wants you to maximize the summation. Notice that no string in this problem could have leading zeros except for string "0". It is guaranteed that at least one character does not appear at the beginning of any string.

The summation may be quite large, so you should output it in modulo 109+7.

Input

The input contains multiple test cases.

For each test case, the first line contains one positive integers n, the number of strings. (1≤n≤100000)

Each of the next n lines contains a string si consisting of only lower case letters. (1≤|si|≤100000,∑|si|≤106)

Output

For each test case, output "Case #x: y" in one line (without quotes), where x indicates the case number starting from 1 and y denotes the answer of corresponding case.

Sample Input

1

a

2

aa

bb

3

a

ba

abc

Sample Output

Case #1: 25

Case #2: 1323

Case #3: 18221

题意:

小写字母'a''z'对应于数字025,但是那个字母对应某一个数字这个是不知道的,但是要求一个字母只能唯一的对应一个数字,给定一些字符串,然后将字符串对应的数字表示出来后将这个数字看作26进制来进行转换位10进制,找出一种对应关系使得这些字符串转换后的数字的和最大。还需要注意的一点就是说每一个字符串如果长度大于1的话,那么他的第一个字母表示的数字不能够是0。

分析:

如果不考虑字符串的前导不能为0的话,还是比较好解决的,定义:

num[i][j]:表示字母(i+'a')在第j个位置出现的次数。

bj[i]:表示字母(i+'a')有没有作为首字母出现过。

sum[i]:表示的是字母(i+'a')的所有后面需要呈上的幂次和,因为不管这个字母表示的数字是几,但是因为这个数字没每一个字符串中的位置都不会变化,也就是说它后面需要乘上的那个幂次不会发生变化。

a[26],含义就是说a[i]=j表示i这个数字对应的是字母(j+'a')

了解了这些定义之后呢,集体的看一下解题思路,

因为进行进制转换的时候都是从后往前计算,所以必须将整个的字符串给逆序,这里运用了一个字符串逆序函数(reverse()),逆序中i后我们就可以很好的统计每个字母在任意一个位置出现的次数。

次数统计好之后,我们就要考虑如何给每一个字母对应值了,能想到的肯定是如果某一个字母它的最高位置越大,那么这个字母所表示的数字应该越大,那么如果说两个字母的最高位置是一样的呢?那么我就就要接着往下比较他们的次高位置,还是一样的道理,一直往下比较下去直到它们在同一个位置出现的次数不一样为止。这些字母是在数组a中进行排序的,拍好后就是这些字母对应的小到大的顺序。

然后我们考虑前导字母表示的数字不能够为数字0,(出现在这种情况也就意味着26个字母都出现了)。这样我们肯定能够在非前导中找到一个对应的数字最小的,然后将它对哦赢得数组看作0,然后其余的对应的数字小于该数字的所有字母对应的数字依次向前对应一位。

代码:(附详细注释)

#include<stdio.h>
#include<iostream>
#include<string.h>
#include <algorithm>
const int L=100020;
const int mod=1e9 + 7;
typedef long long ll;
using namespace std; int num[26][L];///num[i][j]表示第i个字母在位置j出现的次数
int bj[26];///标记字母有没有在首位出现
int Mi[L],sum[L];///分别表示26的幂和某一个字母总共的次方和
int max_len=-1;
int n,Case=0;;
char str[L];///保存输入的字符串
int a[26];///含义就是说a[i]=j表示i这个数字对应的是字母(j+'a')
void inif()///所有的数组完成一个初始化的功能
{
memset(num,0,sizeof(num));
memset(bj,0,sizeof(bj));
memset(sum,0,sizeof(sum));
} ///这个排序也就是相当于将每一个字母所对应的数字按照从小到大的顺序排好
bool cmp(int n1,int n2)/// 用n1,n2分别表示的是两个字母(该字母可以表示为n1+'a',n2+'a'),并不是他们所对应的数字
{
for(int i=max_len-1; i>=0; i--)
{
if(num[n1][i]!=num[n2][i])
return num[n1][i]<num[n2][i];///然后按照这两个字母在同一个位置出现次数按照从小到大排序
}
return 0;
}
void solve()
{
inif();
for(int i=0; i<n; i++)
{
scanf(" %s",str);
int len=strlen(str);
if(len>1)
bj[str[0]-'a']=1;
reverse(str , str + len);///字符串倒置,就是将整个字符串的位置颠倒过来
for(int j=0; j<len; j++)
{
num[str[j]-'a'][j]++;///该字母在当前位出现的次数加加
sum[str[j]-'a']+=Mi[j];
/*
最开始的时候不理解这里是什么意思,就是说不管一个字母表示的数字如何变化,但是它所在的位数是不会
变化的,也就是说它后面需要乘上的26的几次方不会发生变化
*/
if(sum[str[j]-'a']>=mod)
sum[str[j]-'a']%=mod;
}
max_len=max(len,max_len);
}
for(int i=0; i<26; i++)
{
for(int j=0; j<max_len; j++)
{
num[i][j+1]+=num[i][j]/26;
num[i][j]%=26;
}
while(num[i][max_len])///是说最高位上的某个字母的个数已经超过26了,为了计算方便则向前进一位
{
num[i][max_len+1]=num[i][max_len]/26;///看能不能向前进一位
num[i][max_len]%=26;///保留当前位上的个数
max_len++;///字符串总长度及加
}
a[i]=i;///表示的是当前字母所对应的数字,暂时先这样定义,也相当于一个初始化过程
} sort(a,a+26,cmp);///关键在于看懂这个函数的排序规则,排完之后就是每一个字母所对应的数字了 int ans=-1;
for(int i=0; i<26; i++)
{
if(!bj[a[i]])
{
ans=a[i];
break;
}
} int res=0,x=25;
for(int i=25; i>=0; i--)
{
if(a[i]!=ans)
{
// printf("%d %d\n",a[i],sum[a[i]]);
res+=(ll)(x--)*sum[a[i]]%mod;///只有这个字母出现过,sum[a[i]]才有值
res%=mod;
}
} printf("Case #%d: %d\n",++Case,res); }
int main()
{
Mi[0]=1;
for(int i=1; i<L; i++)
{
Mi[i]=(ll)Mi[i-1]*26%mod;
//printf("%d\n",Mi[i]);
}
while(~scanf("%d",&n))
{
solve();
}
}
上一篇:ZOJ 2599 Graduated Lexicographical Ordering ★(数位DP)


下一篇:跟我一起学.NetCore之中间件(Middleware)简介和解析请求管道构建