第七屆藍橋杯大學生C組java決賽題目 密文搜尋
阿新 • • 發佈:2019-01-27
標題:密文搜尋
福爾摩斯從X星收到一份資料,全部是小寫字母組成。
他的助手提供了另一份資料:許多長度為8的密碼列表。
福爾摩斯發現,這些密碼是被打亂後隱藏在先前那份資料中的。
請你編寫一個程式,從第一份資料中搜索可能隱藏密碼的位置。要考慮密碼的所有排列可能性。
資料格式:
輸入第一行:一個字串s,全部由小寫字母組成,長度小於1024*1024
緊接著一行是一個整數n,表示以下有n行密碼,1<=n<=1000
緊接著是n行字串,都是小寫字母組成,長度都為8
要求輸出:
一個整數, 表示每行密碼的所有排列在s中匹配次數的總和。
例如:
使用者輸入:
aaaabbbbaabbcccc
2
aaaabbbb
abcabccc
則程式應該輸出:
4
這是因為:第一個密碼匹配了3次,第二個密碼匹配了1次,一共4次。
解題思路:一看題目,“每行密碼的所有排列在s中匹配次數的總和“,我就想用全排列然後在s中索引,然後發現字母很多重複的,就會有很多重複排列,
然後我就發現可以把s分成很多長度為八的子字串,通過比較子字串中字母使用次數是不是和密碼中字母使用情況相同,如果相同,則該子字串是密碼所有排列中匹配的一種情況。程式碼如下:
import java.util.Scanner; public class Main155 { private static int count = 0; public static void main(String[] args) { Scanner sc = new Scanner(System.in); String s = sc.next(); int n = sc.nextInt(); for (int j = 0; j < n; j++) { char[] ps = sc.next().toCharArray(); for (int i = 0; i < s.length(); i++) { //substring()方法引數的範圍必須在[0,s.length()]中 if (i + 8 <= s.length()) check(s.substring(i, i + 8), ps); } } System.out.println(count); } public static void check(String s, char[] ps) { boolean[] f = new boolean[8]; for (int i = 0; i < s.length(); i++) { for (int j = 0; j < ps.length; j++) { if (!f[j] && s.charAt(i) == ps[j]) { f[j] = true; break; } } } for (int j = 0; j < f.length; j++) { if (!f[j]) { return; } } count++; } }
給出另一種官方演算法,和上面的實現思想大同小異,但是弄懂上面方法作之後,下面的演算法設計可能會讓大家獲益更多,更能體會演算法思想的奇妙:
演算法的設計根據這樣一個特性:
設,n個素數的積為:m
則,m可以唯一地分解為這些素數因子。
只要把每個字母對應一個不同的素數,包含不同字母組合的集團就可以用它們的積表示。
只要判斷積是否相同,就知道是否包含了同一組字母。
import java.util.Scanner; public class Main155 { // 隨便取26個素數,對應26個字母 static int[] prim = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101 }; // 統計匹配個數變數; static int count = 0; // 儲存字串(第一份資料)中所有長度為8的子字串的素數積 static long[] kkey; static int N = 8; public static void main(String[] args) { Scanner sc = new Scanner(System.in); String s = sc.next(); create_kkey(s); int n = sc.nextInt(); int count = 0; for (int j = 0; j < n; j++) { count += find(sc.next()); } System.out.println(count); } public static void create_kkey(String s) { // 每個字母對應一個字母組合,從倒數第七個字母開始子字串不滿足長度為N; kkey = new long[s.length() - N + 1]; kkey[0] = 1; for (int i = 0; i < N; i++) { kkey[0] *= prim[s.charAt(i) - 'a']; } for (int i = 1; i < kkey.length; i++) { kkey[i] = kkey[i - 1] / prim[s.charAt(i - 1) - 'a'] * prim[s.charAt(i + N - 1) - 'a']; } } public static int find(String ps) { long key = 1; for (int i = 0; i < ps.length(); i++) { key *= prim[ps.charAt(i) - 'a']; } int sum = 0; for (int i = 0; i < kkey.length; i++) { if (key == kkey[i]) sum++; } return sum; } }