字典序問題

 問題描述:在數據加密和數據壓縮中需要對特殊的字符串進行編碼。給定的字母表由26個小寫字母組成。該字母表產生的升序字符串是指字符串中字母從左到右出現的次序與字母在字母表中出現的次序相同,且每個字符最多出現1次。例如,a,b,ab,bc,xyz等都是升序字符串。現在對字母表中產生的所有長度不超過6的升序字符串按照字典序排列並編碼如下:

1 2 ... 26 27 28 ...
a b ... z ab ac ...

    對於任意長度不超過6的升序字符中,迅速計算出它在上述字典中的編碼。

    任務:對於給定的長度不超過6的升序字符串,編程計算它在上述字典中的編碼。

    

解題思路:

    本題的關鍵之處在於正確理解題目描述中給出的字典序,其關鍵之處在於首先出現長度爲1的字符串,然後是長度爲2的字符串、……。而在相同長度的字符串中,按照字典序進行。例如,對字符串cfkp這個長度爲4的字符串來講,如果能夠計算出排在它前面的字符串數目,加1就得到該字符串的編碼。排列在該字符串前的字符串可以如下分析:

    (1)長度爲1的字符串、長度爲2的字符串、長度爲3的字符串;

     (2)在以字母c打頭的長度爲4的字符串中,以cd、ce打頭、長度爲4的字符串同樣排列在該字符串前面;而在以cf打頭的長度爲4的字符串中,以cfg、cfh、cfi、cfj打頭的長度爲4的字符串排列在該字符串前面;在以cfk打頭的字符串中,以cfkl、cfkm、cfkn、cfko打頭的長度爲4的字符串同樣在它前面。

    對第(2)種情況進行分析,可以分爲如下幾種情況:

    以cd和ce打頭、長度爲4的字符串數目與以d、e打頭、長度爲3的字符串數目相同;其他情況可以描述爲:以g、h、i、j打頭,長度爲2的字符串數目;以l、m、n、o打頭、長度爲1的字符串數目。

    分析上述情況,對其中的規律進行總結,需要計算的字符串數目可以分爲兩類:

   (1)長度爲k的字符串數目,用g(k)表示;

   (2)以字符ch打頭,長度爲k的字符串數目,用count1(ch,i)表示。

    顯然,有

               count2(k)=sum_{ch=1}^26 count1(ch,k)

    同樣,有如下規律可以發現:

               count1(ch,1)=1

               count1(ch,k)=sum_{i=ch+1}^26 count1(i,k-1)            

             

    在此基礎上,可以計算出每個字符串的編碼。


參考程序如下:


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;


public class Section1_2_1 {


/**
* TODO

* @author LiuYong
* @version 2013-9-3 下午2:52:17
*/
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("請輸入要比較的字符串");
String s = "";
s = scan.next();
int sum1 = 0;
if (s.length() > 6 || s.equals("") || s.length() == 0) {
System.out.println("輸入的字符串長度應該不得超過6");
} else {
Sum sum = new Sum();
Classify c = new Classify();
List<Classify> list = new ArrayList<Classify>();
list = c.Sort(s);
System.out.println(sum.sum1(list)+"  " + sum.sum2(list)+"  " +sum.sum3(list));
sum1 = sum.sum1(list) + sum.sum2(list) + sum.sum3(list);
System.out.println(sum1);
}


}
}


/**
 * 對輸入的字符串進行處理,將每個字母作爲一個對象看待
 * @author LiuYong
 * @version 2013-9-4  上午10:34:37
 */
class Classify {
int length = 0;// 長度,用於進行for循環
double sum = 0;// 數量
int num = 0;// 首字母編號,通過map來處理
List<Classify> list;
//無參構造函數
public Classify() {


}
//有參構造函數
public Classify(int len, int num) {
this.length = len;
this.num = num;
}


/**
* 將字符串中每個字母作爲一個對象封裝到鏈表中返回,將每個字母的編碼位置(即1-26)、後續字符串的長度
* 記錄下來作爲對象的屬性。


* @author LiuYong
* @version 2013-9-2 下午1:41:52
*/
public List<Classify> Sort(String st) {
list = new ArrayList<Classify>();
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
for (int i = 1; i <= 26; i++) {
map.put((char) (97 + i - 1), i);// 用於判斷a——z26個字母的位置即編碼(1-26),此處通過鍵值對一一對應
}
char[] ch = new char[st.length()];//創建一個長度爲字符串長度的字符數組
for (int i = 0; i < st.length(); i++) {
ch[i] = st.charAt(i);//將對應位置的字符放進數組中
Classify cla = new Classify(st.length() - i-1, map.get(ch[i]));
list.add(cla);
}
return list;
}
}


/**
 * 核心計算類
 * @author LiuYong
 * @version 2013-9-4  上午10:44:36
 */
class Deal {


/**
* 計算字母k打頭長度不超過len的字符串個數

* @author LiuYong
* @version 2013-9-3 下午3:48:11
*/
public int count1(int i, int len) {
if (len == 1)
return 1;
else {
int sum = 0;
int j;
for (j = i + 1; j <= 26; j++) {
sum += count1(j, len - 1);
}
return sum;
}
}


/**
* 計算長度不超過len的字符串個數

* @author LiuYong
* @version 2013-9-3 下午3:50:35
*/
public int count2(int len) {
int sum = 0;
for (int i = 1; i <= 26; i++)
sum += count1(i, len);
return sum;
}


}


/**
 * 求和類
 * @author LiuYong
 * @version 2013-9-4  上午10:44:07
 */
class Sum {
Deal d = new Deal();


/**
* 求長度小於字符串長度的個數

* @author LiuYong
* @version 2013-9-3 下午3:58:57
*/
public int sum1(List<Classify> list) {
int sum = 0;
for (int i = 1; i < list.size(); i++) {
sum += d.count2(i);
}
return sum;
}


/**
* 長度爲字符串長度但是在首字符之前的個數

* @author LiuYong
* @version 2013-9-3 下午4:00:51
*/
public int sum2(List<Classify> list) {
int sum = 0;
for (int i = 1; i < list.get(0).num; i++) {
sum += d.count1(i, list.size());
}
return sum;
}


/**
* 首字母相同的且長度不超過字符串長度的
* @author LiuYong
* @version 2013-9-3  下午7:18:33
*/
public int sum3(List<Classify> list) {
int sum = 0;
for (int i = 1; i < list.size(); i++) {
int len = list.get(i - 1).length;
int temp1 = list.get(i - 1).num;
int temp2 = list.get(i).num;
for (int j = temp1 + 1; j < temp2; j++) {
sum += d.count1(j, len);
}
}
if(list.size()==1){
return sum;
}else{
return sum+1;
}
}
}


發佈了16 篇原創文章 · 獲贊 3 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章