字典序問題(演算法課本課後實現題1-2)
題目描述:
在資料加密和資料壓縮中常需要對特殊的字串進行編碼.給定的字母表A由26個小寫英文字母組成,即A={a,b...z}.該字母表產生的升序字串是指定字串中字母從左到右出現的次序與字母在字母表中出現的次序相同,且每個字元最多出現1次.例如,a,b,ab,bc,xyz,等字串是升序字串.現在對字母表A產生的所有長度不超過6的升序字串按照字典序排列並編碼如下
1 2 3 4 ... 26 27 28 ...
a b c d ... z ab ac ...
對於任意長度不超過6的升序字串,迅速計算出它在上述字典中的編碼
資料輸入:檔案第一行是一個正整數k,表示接下來有k行,在接下來的k行中,每行給出一個字串
資料輸出:檔案有k行,每行對應一個字串的編碼
輸入檔案示例:2 輸出檔案示例:1
a 2
b
問題分析
核心思想:構造長度為k的字串的做法是在長度為k-1的字串前加一個字元
題目要求計算字串在字典中的編碼,那麼可以考慮該字串前有多少個字串(一般思想是先將所有字串構造出來),那麼就需要分兩步計算
(1)計算長度小於k的字串的總個數
(2)計算長度等於k但是在所給字串前的字串的個數
假設長度為k的升序字串的總個數為total_sum(k),以第i個字串開始的長度為k的升序字串的個數為section_sum(i,k),則total_sum(k)=
由題 section_sum(i,1)=1(i從1到26),則total_sum(1)==26
section_sum(i,2)==26-i(i從1到26),則total_sum(2)==
由特殊到一般,得出
section_sum(i,k)=(i從1到26),
total_sum(k)=
然後就可以用程式碼實現了,如下
#include<iostream>
#include<cstring>
using namespace std;
int section_sum(int i,int k){
//遞迴求法,求以i開頭長度為k的升序字串的總個數
int sum=0;
if(k==1)
return 1;
for(int j=i+1;j<=26;j++){
sum+=section_sum(j,k-1);
}
return sum;
}
int total_sum(int k){
//長度為k的升序字串總個數
int sum=0;
for(int i=1;i<=26;i++){
sum+=section_sum(i,k);
}
return sum;
}
int main(){
char str[10];
cin>>str;
int ans=0;
int len=strlen(str);
for(int i=1;i<len;i++)//先把所有長度小於所求字串長度的字串的個數求出來
ans+=total_sum(i);
/*int one=str[0]-'a'+1;//第一個字元的順序數
for(int i=1;i<one;i++)//求所有長度等於所求字串長度且首字母在所求首字母之前的字串個數
ans+=section_sum(i,len);*/
for(int i=0,temp=0;i<len;i++){
int num=str[i]-'a'+1;//下一位字元的順序數
int len2=len-i;//獲取當前的長度
for(int j=temp+1;j<num;j++)
ans+=section_sum(j,len2);
temp=num;
}
cout<<"The last number is "<<ans+1<<endl;
return 0;
}