さて、これで求めたい文字列が得られたわけですが、ここでアルゴリズムとして全ての順列について検査しているので、計算時間がO(n!)になってしまいます。
そこで、これを線形時間、つまり、入力文字列数nと出力文字列数mに比例した計算時間O(mn)になるようにしたい。
ということで、与えられた文字列
{"CAA", "AAB", "ABA", "BAB", "ABC", "BCA", "CAB"}
から、次のような、k-1文字の部分文字列の遷移を書いてみます。
そうすると、答えになるのは、このグラフが一筆書きできるもの、つまりオイラーパスを求めればいいということになります。そうなると、あとは一般的なグラフの問題として解くことができます。
で、この問題っていったいなんだったのかというと、バイオインフォマティクスでDNAの配列を決めるための問題で、Sequence By Hybridization(SBH)といいます。
実際のところ、測定誤差があることから、このアルゴリズムはそのままでは使いものにならないようですが、まあ、こんな考え方がベースになっているということです。
実際組もうと思ったのだけど、全ての組み合わせを線形時間で求めようとしたらめんどくさかったので、答えを一組だすものを作ってみました。
public class SBH {
public static void main(String[] args){
System.out.println(sbh(new String[]{"BAB", "ABA", "AAB", "AAA", "BAA"}));
System.out.println(sbh(new String[]{"BAB", "AAB", "AAA", "BAA"}));
System.out.println(sbh(new String[]{"CAA", "AAB", "ABA", "BAB", "ABC", "BCA", "CAB"}));
System.out.println(sbh(new String[]{"CAA", "AAB", "ABA", "BAB", "ABC", "CAB"}));
System.out.println(sbh(new String[]{"CAA", "AAB", "ABA", "BAB", "ABC", "CAB", "CAB", "BBC"}));
}
public static String sbh(String[] s){
String result = "";
if(s == null || s.length == 0) return result;
Map<String, Set<String>> matrix = new HashMap<String, Set<String>>();
Map<String, Set<String>> invmatrix = new HashMap<String, Set<String>>();
for(String str : s){
String from = str.substring(0, str.length() - 1);
String to = str.substring(1);
if(!matrix.containsKey(from)) matrix.put(from, new HashSet<String>());
matrix.get(from).add(to);
if(!invmatrix.containsKey(from)) invmatrix.put(from, new HashSet<String>());
if(!invmatrix.containsKey(to)) invmatrix.put(to, new HashSet<String>());
invmatrix.get(to).add(from);
}
String outover = null;
String inover = null;
for(String key : invmatrix.keySet()){
int outcount = matrix.containsKey(key) ? matrix.get(key).size() : 0;
int incount = invmatrix.get(key).size();
if(outcount == incount){
}else if(outcount == incount + 1){
if(outover != null){
return result;
}
outover = key;
}else if(outcount + 1 == incount){
if(inover != null){
return result;
}
inover = key;
}else{
return result;
}
}
if((outover != null) != (inover != null)) return result;
List<String> results = new LinkedList<String>();
List<String> temp = new LinkedList<String>();
String word = (outover != null) ? outover : matrix.keySet().iterator().next();
result += word.charAt(0);
int insertindex = 0;
while(matrix.size() > 0){
temp.add(word);
if(!matrix.containsKey(word)){
results.addAll(insertindex, temp);
temp.clear();
String next = matrix.keySet().iterator().next();
insertindex = results.indexOf(word);
word = next;
continue;
}
String next = matrix.get(word).iterator().next();
matrix.get(word).remove(next);
if(matrix.get(word).isEmpty()) matrix.remove(word);
word = next;
}
temp.add(word);
results.addAll(insertindex, temp);
for(String str : results) result += str.charAt(1);
return result;
}
}