搜尋和組合數學P1246 編碼
阿新 • • 發佈:2020-08-28
題意簡述
- 要求給字串按照規定編號,字串長度小於等於 \(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; }
還有問題的可以評論。