作者:心里有事1_891 | 来源:互联网 | 2024-11-23 15:19
题目描述:BalalaPower!时间限制:4000/2000MS(Java/Other)内存限制:131072/131072K(Java/Other)。题目背景及问题描述详见正文。
### 题目背景与描述
Talented Mr. Tang 拥有 n 个仅由小写字母组成的字符串。他希望将这些字符串中的每个字符转换为 0 到 25 之间的数字,但不同的字符不能转换为相同的数字,从而可以在 26 进制下计算这些字符串的和。他的目标是使这些字符串的和最大化。
需要注意的是,任何字符串都不能有前导零,除非该字符串本身就是 '0'。保证至少有一个字符不会出现在任何字符串的开头。
由于最终的和可能非常大,因此需要输出结果模 10^9 + 7 的值。
### 输入
输入包含多个测试用例。每个测试用例的第一行包含一个正整数 n(1 ≤ n ≤ 100000),表示字符串的数量。接下来的 n 行每行包含一个字符串 si(1 ≤ |si| ≤ 100000,且所有字符串的总长度不超过 10^6)。
### 输出
对于每个测试用例,输出一行,格式为 "Case #x: y",其中 x 是从 1 开始的测试用例编号,y 是对应测试用例的答案。
### 示例输入
```
1
a
2
aa
bb
3
a
ba
abc
```
### 示例输出
```
Case #1: 25
Case #2: 1323
Case #3: 18221
```
### 解题思路
题目要求将每个字符映射为 0 到 25 之间的数字,使得所有字符串在 26 进制下的和最大化。为了避免前导零的情况,我们需要特别处理那些可能出现在字符串开头的字符。
1. **统计每个字符的权重**:首先,统计每个字符在每个位置上出现的次数,作为该字符的权重。
2. **排序并赋值**:根据权重对字符进行排序,权重大的字符优先赋予较大的数字。
3. **处理前导零**:检查是否有字符出现在字符串的开头,如果有,则调整这些字符的赋值顺序,确保没有前导零。
4. **计算结果**:根据最终的字符映射,计算所有字符串的和,并取模 10^9 + 7 输出。
### 参考代码
```cpp
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const ll mod = 1e9 + 7;
const ll N = 1e5 + 7;
ll n;
string s[N];
ll num[30]; // 最后赋的值
ll mi[N]; // 26 的次幂
ll vis[30]; // 标记某一个字母是否在第一位出现过
struct node {
ll id;
ll num[N]; // 在某个位置出现的次数
bool operator <(const node &a) const {
for (ll j = 100000; j >= 0; j--) {
if (num[j] != a.num[j])
return num[j] > a.num[j];
}
return 0;
}
} p[30];
int main() {
ios::sync_with_stdio(false);
ll q = 1;
mi[0] = 1;
for (ll i = 1; i <= N; i++)
mi[i] = (mi[i - 1] * 26) % mod;
while (cin >> n) {
// 初始化
for (ll i = 0; i <26; i++) {
for (ll j = 0; j <= N; j++)
p[i].num[j] = 0;
p[i].id = i;
vis[i] = 0;
}
// 读入 n 组数据, 从右往左记录次数
for (ll i = 0; i cin >> s[i];
ll l = s[i].size();
if (l > 1) vis[s[i][0] - 'a'] = 1;
for (ll k = l - 1, j = 0; k >= 0; k--, j++) {
ll t = s[i][k] - 'a';
p[t].num[j]++;
}
}
// 进位
for (ll i = 0; i <26; i++)
for (ll j = 0; j <= N; j++) {
p[i].num[j + 1] += p[i].num[j] / 26;
p[i].num[j] %= 26;
}
sort(p, p + 26);
for (ll i = 25; i >= 0; i--)
num[p[25 - i].id] = i; // 排好序之后依次赋值
ll t = 25;
while (vis[p[t].id] && t) { // 判断有没有出现前导 0,如果出现的话,就更改赋值的顺序
swap(num[p[t].id], num[p[t - 1].id]);
t--;
}
ll ans = 0;
for (ll i = 0; i ll l = s[i].size();
for (ll j = 0; j ans = (ans + num[s[i][j] - 'a'] * mi[l - 1 - j] % mod) % mod;
}
printf("Case #%lld: %lld\n", q++, ans);
}
return 0;
}
```