1-2 字典序问题(算法设计与分析)

文章讲述了如何根据字典序规则,设计一种算法来计算字符串的编码,通过组合数计算位数小于和等于字符串长度的字符串个数,确保编码的正确性。编码过程中考虑到字符的升序排列和剩余位置的选择。作者给出了C++代码示例。
摘要由CSDN通过智能技术生成

一开始我的思路是按照进制转换,a~z的进制是26,所以ab = 27 * 1 + 1 * 2 = 29

但题目有个条件,即字典序排序,也就是不可能出现诸如“aa”、“ba”的情况,这样就会使得后面的编码向前移动,处理起来很麻烦

解决方案

求某个字符串的编码,可以分析在此之前的字符串有多少个,每个字符串占一个编码,那么个数和编码就一一对应了

以求字符串“cefh”的编码为例:

① 先求出位数< 4 的字符串有多少,即位数为1,位数为2,以及位数为3的总个数

通过组合数来求(因为字典序,所以没有排序需要) 

\rm sum\ +=\ C_{26}^1+C_{26}^2+C_{26}^3

② 接下来求位数 = 4 的字符串从开始到 “cegm” 的个数

首先从第一位看起,统计从 “a” 到 “c”,先是“a * * *”的个数,得到 \large\rm sum\ +=\ C_{26-1}^{4-1},然后统计“b * * * ”,得到 \large\rm sum\ +=\ C_{26-2}^{4-1}

这样的话,我们就把第一位固定到了 “c”,变成了“c * * * ”,接下来就看第二位

因为升序,第n位的起点至少是第n-1位的数+ 1,因此第二位的起点是 c + 1=d

下面的步骤与第一步一样,从“d”到“e”,只需统计 “c d * *”的个数,\large\rm sum\ +=\ C_{26-4}^{4-2}

这样的话,我们就把第二位固定到了 “e”,变成了“c e * *”,接下来看第三位

第三位的起点是 e + 1=f,从“f”到“g”,得到\large\rm sum\ +=\ C_{26-6}^{4-3},这样就把第三位固定到了 “g”,变为“c e g *”

最后第四位的起点 g + 1 = h,从“h”到“m”,得到 \large\rm C_{24-8}^{4-4}=C_{24-9}^{4-4}=\cdots=1\implies sum\ +=\ 1+1\cdots+1

可以发现,到了第 i 位时,只剩下 len - i - 1 位可以自由放数字,之所以减去1,是因为如果剩下所有位都自由放字母,就会超过所求的字符串

例如“abcde”,到了第 2 位“b”,那么就只剩下“c” 和“d”的位置可以自由放字母,如果“e”的位置也随便放,那么就会超过编码
同时,若第 i 位的字母编码为alpha,因为升序要求,只剩下 26 - alpha 个字母可以选择

因此,就是在 26 - alpha 个字母当中,选择出 len - i - 1 个放到位置上(组合数)


设计步骤

一、计算组合数 \large C_{n}^m


C_{n}^m=\dfrac{n\cdot (n-1)\cdots(n-m+1)}{m!}
 

二、计算小于位数len的字符串个数


\rm sum\ +=\ C_{26}^1+C_{26}^2+\cdots+C_{26}^{len-1}
 

三、计算位数等于 len 的字符串从开始到所求字符串之间的个数
for 字符串的第1位 -> len - 1位:
    for 当前位的起点字母 -> 当前位字母的前一个字母:
        sum += C(剩余可选字母个数, 剩余位数)
    end
    确定下一位的起点字母(即当前位字母的后一个字母)
end

四、将输入的字符串编码

比如,给字符串“abz”,则编码为“1 2 26”,借助数组实现

这样是方便之后的比较


代码实现

#include <iostream>
#include <string.h>

using std::cin;
using std::cout;
using std::endl;
using std::string;

int C(int n, int m) {
    int sum1 = 1, sum2 = 1;
     
    for (int i = 2; i < m + 1; ++i)
        sum2 *= i;
    for (int i = n; i > n - m; --i)
        sum1 *= i;
     
    return sum1 / sum2;
}

int main()
{
    int n;
    cin >> n;
    while (n--) {
        string code;
        cin >> code;
        int len = code.length(), sum = 0;
        int code_num[len];
         
        // 1. 将字符串进行编码
        for (int i = 0; i < len; ++i)
            code_num[i] = code[i] - 96;
         
        // 2. 统计位数小于len的个数
        for (int i = 1; i < len; ++i)
            sum += C(26, i);
         
        // 3. 统计位数等于len的字符串到所求之间的个数
        int start = 1;               // 第 x 位的起点,初始化为1
        for (int i = 0; i < len; ++i) {
            for (int j = start; j < code_num[i]; ++j) {
                sum += C(26- j, len - i - 1);
                // 26 - j 表示剩下还有多少字母可以选择
                // len - i - 1 表示剩下多少位置可以放字母
            }
            start = code_num[i] + 1; // 更新下一位的起点,因为升序,所以至少 + 1
        }
        
        cout << sum << endl;
    }
     
    return 0;
}


参考:字典序问题(算法实现—python)_校验字典序的代码-CSDN博客

另解:

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值