1. 程式人生 > 實用技巧 >搜尋和組合數學P1246 編碼

搜尋和組合數學P1246 編碼

  題目連結P1246 編碼

題意簡述

  • 要求給字串按照規定編號,字串長度小於等於 \(6\)
  • 能編號的字串的字母一定是遞增的,規定:
    • \(\mathbf{a}\rightarrow 1\)

    • \(\mathbf{b}\rightarrow 2\)

      \(\cdots\)

    • \(\mathbf{z}\rightarrow 26\)

    • \(\mathbf{ab}\rightarrow 27\)

    • 最後一個編號 \(\mathbf{uvwxyz}\rightarrow313911\)

  • 如果給出的字串字母不遞增,則輸出 \(0\) ,否則輸出對應編號。

暴力列舉

  首先資料範圍很小。先簡單算一下,用組合數計算出每一個長度能編號的字串數目。

  • 長度為 \(1\) 能編號的字串數目為 \(C_{26}^1 = 26\)
  • 長度為 \(2\) 能編號的字串數目為 \(C_{26}^2 = 325\)

  把 \(6\) 個組合數加起來編號就是 \(313911\) ,即最後一個字串 \(\mathbf{uvwxyz}\) 的編號,完全可以列舉每一個可以編號的字串,找到匹配的就輸出編號即可,否則輸出 \(0\)

Code:

#include <bits/stdc++.h>
#define FOR(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
char s[10],test[10];//s記錄詢問的字串,test記錄嘗試的字串
int n,id;  //n記錄詢問字串長度,id為編號

//last記錄前一個字母,step記錄遞迴層數,列舉下一位
void dfs(char last, int step){
    if(!step) return;
    for (char i = last + 1; i <= 'z'; i++) {
        test[step] = i;
        if(step == 1) {
            id++;     //列舉到最後一位編號才加一
            int flag = 1;
            FOR(j,1,n) if(s[j] != test[n - j + 1]) flag = 0; //逐位比對,注意test是逆著記錄的
            if(flag) {cout << id;exit(0);}
        }
        dfs(i,step-1);
    }
}

signed main(){
    scanf("%s", s + 1);
    n = strlen(s + 1);
    FOR(i,1,n)
        dfs('a'-1,i);

    cout<<0;  //在dfs中未找到,說明字串不滿足要求
    return 0;
}

組合數學

  這題如果資料量較大可以用組合數學解決。我們先算出長度為 \(1\)~ \(n-1\) 的能編號字串的總數,最後再解決長度為 \(n\) 的字串情況。涉及的細節可能比較多,我儘可能註釋了。

Code:

#include <bits/stdc++.h>
#define FOR(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
char s[10];//ch記錄詢問的字串
int n,id,c[30][30];  //n記錄詢問字串長度

signed main(){
    FOR(i,0,26) c[i][0] = 1;
    FOR(i,1,26) FOR(j,1,26) c[i][j] = c[i-1][j]+c[i-1][j-1];//組合數遞推式,
    scanf("%s", s + 1);
    n = strlen(s + 1);
    //處理長度小於n的字串編號
    FOR(i,1,n-1) {
        if(s[i] >= s[i+1]) {
            cout<<0;//不升序排列就直接輸出0
            exit(0);
        }
        id += c[26][i]; //從26個字母中選i個,一定可以排成遞增的字串
    }
    int j ;
    FOR(i,1,n){
        //j要分情況賦值,前i-1位已固定為s[1]到s[i-1](i != 1)
        if(i == 1) j = 'a';
        else j = s[i-1]+1;

        for(;j < s[i];j++)
            id += c['z'-j][n-i]; //在第i位為字母j的情況下,後面n-i位的組合情況。
    }
    cout<<id+1; //記得加一
    return 0;
}

  還有問題的可以評論。