传送门:编码 - 洛谷
题目描述
编码工作常被运用于密文或压缩传输。这里我们用一种最简单的编码方式进行编码:把一些有规律的单词编成数字。
字母表中共有 2626 个字母 a,b,c,⋯ ,za,b,c,⋯,z,这些特殊的单词长度不超过 66 且字母按升序排列。把所有这样的单词放在一起,按字典顺序排列,一个单词的编码就对应着它在字典中的位置。
例如:
- a→1;
- b→2;
- z→26;
- ab→27;
- ac→28。
你的任务就是对于所给的单词,求出它的编码。
输入格式
仅一行,被编码的单词。
输出格式
仅一行,对应的编码。如果单词不在字母表中,输出 00。
输入输出样例
输入 #1复制
ab
输出 #1复制
27
思路分析:
题目说了,这个单词最长也就6个字母,并且单词是按照升序排序的。那么其实我们可以先不用管排列的问题,我们只需要考虑每种情况会有多少种组合就行了,因为每种字母组合就只有一种排列的结果(那就是升序排序,如:{ a , b }这个字母组合只有ab这样一种排列可能)。
易知,由3个字母组合而成的字母在字母表中的顺序肯定比由1或2个字母组合而成的单词的要后面,所以我们可以先计算长度为1~len-1的单词数目。
统计对同样长度为len但是小于题目所给单词的单词数目:
例如如果我们已经确定了第一位的字母是‘a’,那么我们就要从25位字母中选取len-1个字母。类似的,如果我们已经确定了第i个位置的字母j,那么我们就要从'z'-j个字母中选取len-i-1个(因为字符串是从第0位开始计算的,所以i就是从0开始增长的)。
ACcode:
#include<bits/stdc++.h>
using namespace std;
#define int long long
string str;
int solve(int n, int m) {//组合数
if (!m) return 1;
int t = 1;
for (int i = n; i > n - m; i--) {
t *= i;
}
for (int i = 1; i <= m; i++) {
t /= i;
}
return t;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> str;
int ans = 0, len = str.size();
for (int i = 1; i < len; i++) {
if (str[i] <= str[i - 1]) {//判断单词是否在字母表中
cout << 0;
exit(0);
}
}
//计算出位数比str小的单词数
for (int i = 1; i < len; i++) {
ans += solve(26, i);
//从26位字母中选取i个字母组合成一个单词
/*注意:我们只是选取字母,并未对字母进行排序,而依照题目要求我们知道对于每种选取结果
只有一种可能,那就是按照升序排列*/
}
for (int i = 0; i < len; i++) {
for (char j = (i == 0 ? 'a' : str[i - 1] + 1); j < str[i]; j++) {
ans += solve('z' - j, len - i - 1);
//'z'-j:ASCII码大于j的字母个数
//len-i-1:str中第i个字母后的剩余字母数目
}
}
//ans是小于str的单词数目之和,所以要再自增一位,str是第ans+1个单词
cout << ++ans;
return 0;
}
附录:本文借鉴了洛谷的Alex_Wei大佬的题解,大家可以移步洛谷查看原文