1. 程式人生 > >POJ-2159(Ancient Cipher)

POJ-2159(Ancient Cipher)

Ancient Cipher

 

POJ連結:http://poj.org/problem?id=2159

//原題是英文,我用谷歌翻譯讓大家更好的理解這題是意思,此題應該算是金鑰問題裡面簡單的題^_^

描述
古羅馬帝國擁有強大的政府體系,各部門包括一個祕密服務部門。各省和首都之間以加密形式傳送重要檔案,以防止竊聽。那些時代最流行的密碼是所謂的替換密碼和置換密碼。
替換密碼將每個字母的所有出現更改為其他字母。所有字母的替代品必須不同。對於某些字母,替換字母可能與原始字母重合。例如,應用替換密碼將所有字母從“A”更改為“Y”到字母表中的下一個字母,並將“Z”更改為“A”,將訊息“VICTORIOUS”更改為“WJDUPSJPVT”。
置換密碼對訊息的字母應用一些置換。例如,將置換<2,1,5,4,3,7,6,10,9,8>應用於訊息“VICTORIOUS”,得到訊息“IVOTCIRSUO”。
很快就注意到,單獨應用,替換密碼和置換密碼都相當弱。但是當它們合併時,它們在那些時候足夠強大。因此,首先使用替換密碼對最重要的訊息進行加密,然後使用置換密碼對結果進行加密。使用上述密碼的組合加密訊息“VICTORIOUS”,獲得訊息“JWPUDJSTVP”。
考古學家最近發現這條資訊刻在石板上。乍一看它似乎完全沒有意義,因此建議使用一些替換和置換密碼對訊息進行加密。他們推測了加密的原始郵件的可能文字,現在他們想檢查他們的猜想。他們需要一個計算機程式來完成它,所以你必須寫一個。

輸入


輸入包含兩行。第一行包含刻在板上的資訊。在加密之前,所有空格和標點符號都被刪除,因此加密的郵件只包含英文字母的大寫字母。第二行包含推測在第一行的訊息中加密的原始訊息。它還只包含英文字母的大寫字母。
輸入的兩條線的長度相等,不超過100。

輸出
如果輸入檔案第一行上的訊息可能是第二行加密訊息的結果,則輸出“YES”,否則輸出“NO”。

樣本輸入
JWPUDJSTVP
VICTORIOUS

樣本輸出
YES

解題思路:

此題中的某個字串經過一些交換和替換的操作後,新產生的字串相當於原碼的編碼,剛開始看感覺既交換還要替換相當複雜,其實只用看兩個字串每種字元出現的頻率是否一致即可,我用一個表格給大家看清楚:

所有很容易看出規律,題目要求全是大寫字母,更簡單了一些(‘A’ 65 ~ ‘Z’ 90),可以將字串每個元素減去‘A’,結果正好包含在字母序號(0~25)中,相當於陣列的下標,遍歷一遍統計好出現的次數,再用一個排序函式(最好從大到小排,比對時效率高一些),兩個字串的頻率陣列排序好之後,直接進行一一比對即可。

 

 初版的程式碼:(用的是氣泡排序)

 

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #define N 100
 5 #define S 27  
 6 char a[N], b[N];
 7 int count1[S], count2[S];
 8 void calculate() {//統計字串中字元出現的頻率 
 9     int len1 = strlen(a);
10     int len2 = strlen(b);
11 int count = 0; 12 for (int i = 0; i < len1; i++) { 13 if (a[i] >= 'A'&&a[i] <= 'Z') 14 { 15 count = a[i] - 'A'; 16 count1[count]++; 17 } 18 } 19 for (int j = 0; j < len2; j++) { 20 if (b[j] >= 'A'&&b[j] <= 'Z') 21 { 22 count = b[j] - 'A'; 23 count2[count]++; 24 } 25 } 26 } 27 void sort(int q[]) {//氣泡排序 28 int temp; 29 for (int i = 0; i < S - 1; ++i) 30 for (int j = 0; j < S - i - 1; ++j) 31 { 32 if (q[j] > q[j + 1]) 33 { 34 temp = q[j]; 35 q[j] = q[j + 1]; 36 q[j + 1] = temp; 37 } 38 } 39 } 40 int main() 41 { 42 gets(a); 43 gets(b); 44 calculate(); 45 sort(count1); 46 sort(count2); 47 int flag = 1; 48 for (int i = 0; i < S; i++) { 49 if (count1[i] != count2[i]) 50 { 51 flag = 0; 52 break; 53 } 54 } 55 if (flag == 1) 56 printf("YES\n"); 57 else 58 printf("NO\n"); 59 return 0; 60 }

 

 

其實還可以進行優化,不需要用兩個字串陣列來存,只用一個字元陣列即可,用完直接輸入新的字串替換原來的,排序改用快速排序,時間複雜度:O(nlogn)

優化後的程式碼:

 1 #include<stdio.h>  
 2 #include<stdlib.h>  
 3 int compare(const void*a, const void*b)
 4 {
 5     int *pa = (int *)a;
 6     int *pb = (int *)b;
 7     return *pa - *pb;//從大到小來排 
 8 }
 9 int main()
10 {
11     int i, a[27], b[27];
12     char s[200];
13     for (i = 0; i < 27; i++)
14         a[i] = b[i] = 0;
15     gets(s);
16     for (i = 0; s[i]; i++)
17         a[s[i] - 'A']++;
18     gets(s);
19     for (i = 0; s[i]; i++)
20         b[s[i] - 'A']++;
21     qsort(a, 27, sizeof(int), compare);
22     qsort(b, 27, sizeof(int), compare);
23     for (i = 0; i < 27; i++)
24         if (a[i] != b[i])
25             break;
26     if (i > 26)
27         puts("YES\n");
28     else
29         puts("NO\n");
30     return 0;
31 }

以上程式碼均以AC,不知道大家做這題時先想到的是那個呢

歡迎大家評論,有什麼疑惑或錯誤,望指出O(∩_∩)O