fork(1) download
  1. import java.io.BufferedReader;
  2. import java.io.FileInputStream;
  3. import java.io.InputStreamReader;
  4. import java.util.LinkedHashMap;
  5. import java.util.List;
  6. import java.util.ArrayList;
  7. import java.util.Map;
  8. import java.util.HashMap;
  9. import java.lang.StringBuilder;
  10. import java.io.IOException;
  11.  
  12. class Ideone{
  13. static Map<Integer, List> sDictionaryMap = new HashMap<Integer, List>();
  14. static boolean debug = false;
  15.  
  16. public static void main (String args[]) {
  17.  
  18. try{
  19.  
  20.  
  21. String input;
  22. String section = null;
  23.  
  24. List<String> secretList = new ArrayList<String>();
  25.  
  26. while((input=br.readLine())!=null){
  27. if(input.startsWith("//"))
  28. {
  29. section = input;
  30.  
  31. continue;
  32. }
  33. else if(section !=null)
  34. {
  35. if(section.equals("//dict"))
  36. {
  37.  
  38. List dict = null;
  39. if(!sDictionaryMap.containsKey(input.length()))
  40. {
  41. dict = new ArrayList();
  42. sDictionaryMap.put(input.length(), dict);
  43. }
  44. else
  45. {
  46. dict = sDictionaryMap.get(input.length());
  47. }
  48. dict.add(input);
  49. }
  50. else if (section.equals("//secret"))
  51. {
  52. secretList.add(input);
  53. }
  54. }
  55.  
  56. }
  57.  
  58. long start = System.currentTimeMillis();
  59. for(int i=0; i<secretList.size(); i++)
  60. {
  61.  
  62. String secretLine = secretList.get(i);
  63. StringBuilder sb = new StringBuilder();
  64. sb.append(secretLine + " = ");
  65. sb.append(new SubstitutionCiphersDecoder(sDictionaryMap).decode(secretLine, true));
  66. System.out.println(sb.toString());
  67.  
  68. if(debug)
  69. System.out.println();
  70.  
  71. }
  72.  
  73. long end = System.currentTimeMillis();
  74. if(debug)
  75. System.out.println("execution time: " + (end - start) +"ms");
  76.  
  77. }catch(IOException io){
  78. io.printStackTrace();
  79. }
  80. }
  81.  
  82. public static class SubstitutionCiphersDecoder
  83. {
  84. Map<Integer, List> mDictionaryMap = new HashMap<Integer, List>();
  85. Map<Character, Character> mCharMap = new LinkedHashMap<Character, Character>();
  86.  
  87. /**
  88.   * Class used to decrypt substitution ciphers used to encrypt words
  89.   * @param dictionary of all the valid wor
  90.   * 02f0196faf9446333baf84c6bef05dd9ds
  91.   */
  92. public SubstitutionCiphersDecoder(Map<Integer, List> dictionary)
  93. {
  94. mDictionaryMap = dictionary;
  95. }
  96.  
  97. /**
  98.   * Decode the secret with a specific mapping
  99.   * @param secret the encoded string, can be a word or several words separated by a space
  100.   * @param resetMapping tells if the characters map need to be rebuild from scratch
  101.   * @return the decoded string with all the known char from mapping, keep the unknown ones
  102.   */
  103. public String decode(String secret, boolean resetMapping)
  104. {
  105. if(resetMapping)
  106. buildCharMap(secret);
  107. return decode(secret, mCharMap);
  108. }
  109.  
  110. /**
  111.   * Decode the secret with a specific mapping
  112.   * @param secret the encoded string, can be a word or several words separated by a space
  113.   * @param mapping the characters mapping to decode the secret with
  114.   * @return the decoded string with all the known char from mapping, keep the unknown ones
  115.   */
  116. public String decode(String secret, Map<Character, Character> mapping)
  117. {
  118. StringBuilder result = new StringBuilder();
  119. for(int i=0; i<secret.length(); i++)
  120. {
  121. result.append(decodeChar(secret.charAt(i), mapping));
  122. }
  123.  
  124. return result.toString();
  125. }
  126.  
  127. /**
  128.   * Decode a character with a specific mapping
  129.   * @param secretChar the encoded character
  130.   * @param mapping the characters mapping to decode the secret with
  131.   * @return the decoded character if it is known by the mapping, or the original encoded character otherwise
  132.   */
  133. public Character decodeChar(Character secretChar, Map<Character, Character> mapping)
  134. {
  135. Character decodedChar = mapping.get(secretChar);
  136. return ((decodedChar!=null)?decodedChar:secretChar);
  137. }
  138.  
  139. /**
  140.   * Test if the secret can be decoded
  141.   * @param secretWord the encoded string
  142.   * @param mapping the characters mapping to decode the secret with
  143.   * @return true if the decoded string exist in the dictionary, false otherwise
  144.   */
  145. private boolean canDecode(String secretWord, Map<Character, Character> mapping)
  146. {
  147. List potentialMatches = mDictionaryMap.get(secretWord.length());
  148. return potentialMatches.contains(decode(secretWord, mapping));
  149. }
  150.  
  151. /**
  152.   * Build the characterMap, for each word in the secret line, try a brute force attack to reveal the character mapping
  153.   * @param secretLine is the encoded string
  154.   */
  155. private void buildCharMap(String secretLine)
  156. {
  157. //Reset charMap
  158. mCharMap.clear();
  159. mCharMap.put(' ', ' ');
  160.  
  161. String[] split = secretLine.split(" ");
  162. for(String secretWord:split)
  163. {
  164. if(debug)
  165. System.out.println("secretWord:"+secretWord);
  166.  
  167. List<String> potentialMatches = mDictionaryMap.get(secretWord.length());
  168. for(String potentialMatch : potentialMatches)
  169. {
  170.  
  171. if(debug)
  172. System.out.println("fix mapping for potentialMatch:"+potentialMatch);
  173.  
  174. Map<Character, Character> fixedCharMapAttempt = new HashMap<Character, Character>(mCharMap);
  175. for(int index=0; index<potentialMatch.length(); index++)
  176. {
  177. //if mapping doesn't not yet exist for the encoded char, or if the character already mapped is not valid for another encountered character
  178. if(!fixedCharMapAttempt.containsKey(secretWord.charAt(index)) || !decodeChar(secretWord.charAt(index), fixedCharMapAttempt).equals(potentialMatch.charAt(index)) )
  179. {
  180. //Same character should not be mapped twice, by two different key.
  181. if(!fixedCharMapAttempt.containsValue(potentialMatch.charAt(index)))
  182. {
  183. fixedCharMapAttempt.put(secretWord.charAt(index), potentialMatch.charAt(index));
  184.  
  185. if(debug)
  186. System.out.println("mapping for:" + secretWord.charAt(index) + " is " + potentialMatch.charAt(index) + " decode:"+decode(secretWord, fixedCharMapAttempt)+ " isInDict:"+canDecode(secretWord, fixedCharMapAttempt));
  187. }
  188. }
  189.  
  190. }
  191.  
  192. //save the character mapping only if is usefull
  193. if(canDecode(secretWord, fixedCharMapAttempt))
  194. mCharMap.putAll(fixedCharMapAttempt);
  195.  
  196. else if(debug)
  197. System.out.println("reverting...");
  198. }
  199. }
  200.  
  201. if(debug)
  202. System.out.println("buildCharMap:"+mCharMap);
  203.  
  204. }
  205. }
  206.  
  207. }
Success #stdin #stdout 0.07s 380224KB
stdin
//dict
hello
there
yello
thorns
//secret
12334 51272
12334 514678
stdout
12334 51272 = hello there
12334 514678 = hello thorns