Problem 79
Passcode derivation
A common security method used for online banking is to ask the user for three random characters from a passcode. For example, if the passcode was 531278, they may ask for the 2nd, 3rd, and 5th characters; the expected reply would be: 317.
The text file, keylog.txt, contains fifty successful login attempts.
Given that the three characters are always asked for in order, analyse the file so as to determine the shortest possible secret passcode of unknown length.
密码推断
网上银行常用的一种密保手段是向用户询问密码中的随机三位字符。例如,如果密码是531278,询问第2、3、5位字符,正确的回复应当是317。
在文本文件keylog.txt中包含了50次成功登陆的正确回复。
假设三个字符总是按顺序询问的,分析这个文本文件,给出这个未知长度的密码最短的一种可能。
package projecteuler;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import junit.framework.TestCase;
public class Prj79 extends TestCase {
public static final String FILE = "E:\\whua\\mathWorkspace\\test_jgraph\\src\\projecteuler\\Prj79.txt";
/**
* 前提是不存在重复的数字,使用拓扑排序
*/
public void testPasscodeDerivation() {
List<int[]> dataList = readInts();
Set<Edge> set = new HashSet<Edge>();
for (int i = 0; i < dataList.size(); i++) {
int[] tp = dataList.get(i);
{
Edge e1 = new Edge(tp[0], tp[1]);
Edge e2 = new Edge(tp[1], tp[2]);
set.add(e1);
set.add(e2);
}
}
TopSort topSort = new TopSort();
List<Integer> orders = topSort.topSort(set);
for (int i = 0; i < orders.size(); i++) {
System.out.print(orders.get(i));
}
}
public static class TopSort {
public List<Integer> topSort(Set<Edge> set) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Edge edge : set) {
int to = edge.to;
int from = edge.from;
if (!map.containsKey(from)) {
map.put(from, 0);
}
if (!map.containsKey(to)) {
map.put(to, 0);
}
map.put(to, map.get(to) + 1);
}
List<Integer> ret = new ArrayList<Integer>();
Set<Edge> copy = new HashSet<Edge>(set);
while (true) {
int from = findInDegreeIsZero(copy, map);
copy = removeEdgeByFromEdge(from, copy, map);
ret.add(from);
if (copy.size() < 1) {
ret.add(Integer.parseInt(map.keySet().toArray()[0]
.toString()));
break;
}
if (findInDegreeIsZero(copy, map) == -1 && copy.size() < 1) {
throw new RuntimeException("cycle occured!!!");
}
}
return ret;
}
private Set<Edge> removeEdgeByFromEdge(int from, Set<Edge> copy,
Map<Integer, Integer> map) {
Set<Edge> ret = new HashSet<Edge>();
for (Edge _edge : copy) {
if (_edge.from != from) {
ret.add(_edge);
} else {
map.put(_edge.to, map.get(_edge.to) - 1);
}
}
map.remove(from);
return ret;
}
private int findInDegreeIsZero(Set<Edge> copy, Map<Integer, Integer> map) {
for (Edge edge : copy) {
if (map.get(edge.from) == 0) {
return edge.from;
}
}
return -1;
}
}
public static class Edge {
public int from;
public int to;
public Edge(int from, int to) {
super();
this.from = from;
this.to = to;
}
@Override
public String toString() {
return "Edge [from=" + from + ", to=" + to + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + from;
result = prime * result + to;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Edge other = (Edge) obj;
return other.from == from && other.to == to;
}
}
List<int[]> readInts() {
try {
List<int[]> ret = new ArrayList<int[]>();
BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(FILE)));
String str = "";
while ((str = reader.readLine()) != null) {
str = str.trim();
int[] tp = new int[3];
tp[0] = Integer.parseInt(String.valueOf(str.charAt(0)));
tp[1] = Integer.parseInt(String.valueOf(str.charAt(1)));
tp[2] = Integer.parseInt(String.valueOf(str.charAt(2)));
ret.add(tp);
}
reader.close();
return ret;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}