Uzunluk bir kriter olarak listelendiğinden, burada 1681 karakterdeki golf edilmiş sürüm (muhtemelen% 10 oranında iyileştirilebilir):
import java.io.*;import java.util.*;public class W{public static void main(String[]
a)throws Exception{int n=a.length<1?5:a[0].length(),p,q;String f,t,l;S w=new S();Scanner
s=new Scanner(new
File("sowpods"));while(s.hasNext()){f=s.next();if(f.length()==n)w.add(f);}if(a.length<1){String[]x=w.toArray(new
String[0]);Random
r=new Random();q=x.length;p=r.nextInt(q);q=r.nextInt(q-1);f=x[p];t=x[p>q?q:q+1];}else{f=a[0];t=a[1];}H<S>
A=new H(),B=new H(),C=new H();for(String W:w){A.put(W,new
S());for(p=0;p<n;p++){char[]c=W.toCharArray();c[p]='.';l=new
String(c);A.get(W).add(l);S z=B.get(l);if(z==null)B.put(l,z=new
S());z.add(W);}}for(String W:A.keySet()){C.put(W,w=new S());for(String
L:A.get(W))for(String b:B.get(L))if(b!=W)w.add(b);}N m,o,ñ;H<N> N=new H();N.put(f,m=new
N(f,t));N.put(t,o=new N(t,t));m.k=0;N[]H=new
N[3];H[0]=m;p=H[0].h;while(0<1){if(H[0]==null){if(H[1]==H[2])break;H[0]=H[1];H[1]=H[2];H[2]=null;p++;continue;}if(p>=o.k-1)break;m=H[0];H[0]=m.x();if(H[0]==m)H[0]=null;for(String
v:C.get(m.s)){ñ=N.get(v);if(ñ==null)N.put(v,ñ=new N(v,t));if(m.k+1<ñ.k){if(ñ.k<ñ.I){q=ñ.k+ñ.h-p;N
Ñ=ñ.x();if(H[q]==ñ)H[q]=Ñ==ñ?null:Ñ;}ñ.b=m;ñ.k=m.k+1;q=ñ.k+ñ.h-p;if(H[q]==null)H[q]=ñ;else{ñ.n=H[q];ñ.p=ñ.n.p;ñ.n.p=ñ.p.n=ñ;}}}}if(o.b==null)System.out.println(f+"\n"+t+"\nOY");else{String[]P=new
String[o.k+2];P[o.k+1]=o.k-1+"";m=o;for(q=m.k;q>=0;q--){P[q]=m.s;m=m.b;}for(String
W:P)System.out.println(W);}}}class N{String s;int k,h,I=(1<<30)-1;N b,p,n;N(String S,String
d){s=S;for(k=0;k<d.length();k++)if(d.charAt(k)!=S.charAt(k))h++;k=I;p=n=this;}N
x(){N r=n;n.p=p;p.n=n;n=p=this;return r;}}class S extends HashSet<String>{}class H<V>extends
HashMap<String,V>{}
Paket adlarını ve yöntemlerini kullanan ve sınıfları yalnızca takma adlara uyarmayan veya genişletmeyen ungolfed sürümü:
package com.akshor.pjt33;
import java.io.*;
import java.util.*;
// WordLadder partially golfed and with reduced dependencies
//
// Variables used in complexity analysis:
// n is the word length
// V is the number of words (vertex count of the graph)
// E is the number of edges
// hash is the cost of a hash insert / lookup - I will assume it's constant, but without completely brushing it under the carpet
public class WordLadder2
{
private Map<String, Set<String>> wordsToWords = new HashMap<String, Set<String>>();
// Initialisation cost: O(V * n * (n + hash) + E * hash)
private WordLadder2(Set<String> words)
{
Map<String, Set<String>> wordsToLinks = new HashMap<String, Set<String>>();
Map<String, Set<String>> linksToWords = new HashMap<String, Set<String>>();
// Cost: O(Vn * (n + hash))
for (String word : words)
{
// Cost: O(n*(n + hash))
for (int i = 0; i < word.length(); i++)
{
// Cost: O(n + hash)
char[] ch = word.toCharArray();
ch[i] = '.';
String link = new String(ch).intern();
add(wordsToLinks, word, link);
add(linksToWords, link, word);
}
}
// Cost: O(V * n * hash + E * hash)
for (Map.Entry<String, Set<String>> from : wordsToLinks.entrySet()) {
String src = from.getKey();
wordsToWords.put(src, new HashSet<String>());
for (String link : from.getValue()) {
Set<String> to = linksToWords.get(link);
for (String snk : to) {
// Note: equality test is safe here. Cost is O(hash)
if (snk != src) add(wordsToWords, src, snk);
}
}
}
}
public static void main(String[] args) throws IOException
{
// Cost: O(filelength + num_words * hash)
Map<Integer, Set<String>> wordsByLength = new HashMap<Integer, Set<String>>();
BufferedReader br = new BufferedReader(new FileReader("sowpods"), 8192);
String line;
while ((line = br.readLine()) != null) add(wordsByLength, line.length(), line);
if (args.length == 2) {
String from = args[0].toUpperCase();
String to = args[1].toUpperCase();
new WordLadder2(wordsByLength.get(from.length())).findPath(from, to);
}
else {
// 5-letter words are the most interesting.
String[] _5 = wordsByLength.get(5).toArray(new String[0]);
Random rnd = new Random();
int f = rnd.nextInt(_5.length), g = rnd.nextInt(_5.length - 1);
if (g >= f) g++;
new WordLadder2(wordsByLength.get(5)).findPath(_5[f], _5[g]);
}
}
// O(E * hash)
private void findPath(String start, String dest) {
Node startNode = new Node(start, dest);
startNode.cost = 0; startNode.backpointer = startNode;
Node endNode = new Node(dest, dest);
// Node lookup
Map<String, Node> nodes = new HashMap<String, Node>();
nodes.put(start, startNode);
nodes.put(dest, endNode);
// Heap
Node[] heap = new Node[3];
heap[0] = startNode;
int base = heap[0].heuristic;
// O(E * hash)
while (true) {
if (heap[0] == null) {
if (heap[1] == heap[2]) break;
heap[0] = heap[1]; heap[1] = heap[2]; heap[2] = null; base++;
continue;
}
// If the lowest cost isn't at least 1 less than the current cost for the destination,
// it can't improve the best path to the destination.
if (base >= endNode.cost - 1) break;
// Get the cheapest node from the heap.
Node v0 = heap[0];
heap[0] = v0.remove();
if (heap[0] == v0) heap[0] = null;
// Relax the edges from v0.
int g_v0 = v0.cost;
// O(hash * #neighbours)
for (String v1Str : wordsToWords.get(v0.key))
{
Node v1 = nodes.get(v1Str);
if (v1 == null) {
v1 = new Node(v1Str, dest);
nodes.put(v1Str, v1);
}
// If it's an improvement, use it.
if (g_v0 + 1 < v1.cost)
{
// Update the heap.
if (v1.cost < Node.INFINITY)
{
int bucket = v1.cost + v1.heuristic - base;
Node t = v1.remove();
if (heap[bucket] == v1) heap[bucket] = t == v1 ? null : t;
}
// Next update the backpointer and the costs map.
v1.backpointer = v0;
v1.cost = g_v0 + 1;
int bucket = v1.cost + v1.heuristic - base;
if (heap[bucket] == null) {
heap[bucket] = v1;
}
else {
v1.next = heap[bucket];
v1.prev = v1.next.prev;
v1.next.prev = v1.prev.next = v1;
}
}
}
}
if (endNode.backpointer == null) {
System.out.println(start);
System.out.println(dest);
System.out.println("OY");
}
else {
String[] path = new String[endNode.cost + 1];
Node t = endNode;
for (int i = t.cost; i >= 0; i--) {
path[i] = t.key;
t = t.backpointer;
}
for (String str : path) System.out.println(str);
System.out.println(path.length - 2);
}
}
private static <K, V> void add(Map<K, Set<V>> map, K key, V value) {
Set<V> vals = map.get(key);
if (vals == null) map.put(key, vals = new HashSet<V>());
vals.add(value);
}
private static class Node
{
public static int INFINITY = Integer.MAX_VALUE >> 1;
public String key;
public int cost;
public int heuristic;
public Node backpointer;
public Node prev = this;
public Node next = this;
public Node(String key, String dest) {
this.key = key;
cost = INFINITY;
for (int i = 0; i < dest.length(); i++) if (dest.charAt(i) != key.charAt(i)) heuristic++;
}
public Node remove() {
Node rv = next;
next.prev = prev;
prev.next = next;
next = prev = this;
return rv;
}
}
}
Gördüğünüz gibi işletme maliyeti analizi O(filelength + num_words * hash + V * n * (n + hash) + E * hash)
. Bir karma tablo ekleme / aramanın sabit zaman olduğu varsayımı kabul ederseniz, bu O(filelength + V n^2 + E)
. SOWPODS'taki grafiklerin belirli istatistikleri, çoğu için O(V n^2)
gerçekten egemen olduğu anlamına gelir .O(E)
n
Örnek çıktılar:
IDOLA, IDOLS, IDYLS, ODYLS, ODALS, OVALS, OVELS, OVENS, EVENS, ETENS, STENS, SKENS, SKINS, SPINS, Omurga, 13
WICCA, PROSY, OY
BRINY, BRINS, TRINS, TAIN, TARNS, İPLİK, YAWNS, YAWPS, YAPPS, 7
GALES, GASES, GASTS, GESTS, GESTE, GESSE, DESSE, 5
SURES, DURES, DUNES, DINES, DINGS, DINGY, 4
LICHT, LIGHT, BIGHT, BIGOT, BIGOS, BIROS, GIROS, KIZ, GURNS, GUANS, GUANA, RUANA, 10
SARGE, SERGE, SERRE, SERRS, SEERS, DEERS, BOYALAR, OYERS, OVERS, OVELS, OVALS, ODALS, ODYLS, IDYLS, 12
KEIRS, SEIRS, SEERS, BIRA, BRERS, BRERE, BREME, CREME, CREPE, 7
Bu, en uzun yolu olan 6 çiftten biridir:
KAZANÇ, EN ÇOK, DÜNYA, SAIREST, SAIDEST, SADDEST, MADDEST, MIDDEST, MILDEST, WILDEST, WILIEST, WANIEST, WANEST, CANIEST, KANTEST, YARIŞMA, KONFEST, KONFRE, KONFERS, CONKERS, PİŞİRİCİLER, COOPERS, COPPERS, POPPERS KANEPELER, HAŞHAŞ, KÖPEKLER, MOPSIES, MOUSIES, MOUSS, POUSSES, PLUSS, PLISSES, PRISSES, PRES, PRES, UREASES, UNEASES, UNCASED, UNCASED, UNBASED, UNBATED, UNMED, UNMETE, UNW, INWEDED, ENWED, ENWEDED ENDEKSLER, HİNDİSTANLAR, KİRALIKLAR, TEŞVİKLER, İNCESLER, INFESTS, INFECTS, INJECTS, 56
Ve en kötü durumda çözünür 8 harfli çiftlerden biri:
PÜSKÜRTME, UNROBING, BOŞALTMA, UNCOPING, UNCAPING, UNCAGING, ENCAGING, ENRAGING, ENRACING, STROYING, STROKING, STOPING, STOPOM, STUM CRIMPING, CRISPING, CRISPINS, CRISPENS, CRISPERS, CRIMPERS, CRAMPERS, CLAMPERS, CLASPERS, CLASHERS, SLASHERS, SLATERS, SMITHERS, SMOTHERS, SOOTHERS, SOUTHERS, MOUTHERS, MOUCHERS, MOCHCHERS, COCHERS, COCHERS, COCHERS, CHCHERS LUNCHERS, LYNCHERS, LYNCHETS, LINCHETS, 52
Şimdi, sorunun tüm gereksinimlerini ortadan kaldırdığımı düşünüyorum, tartışmam.
Bir CompSci için soru, köşeleri kelimeler olan ve kenarları bir harften farklı kelimeleri birbirine bağlayan bir grafik G'deki en kısa yola indirgenir. Grafiği verimli bir şekilde oluşturmak önemsiz değildir - aslında karmaşıklığı O'ya (V n hash + E) azaltmak için tekrar gözden geçirmem gereken bir fikrim var. Benim yaptığım yol, (bir joker karakterli kelimelere karşılık gelen) fazladan köşe noktaları ekleyen ve söz konusu grafiğe homeomorfik bir grafik oluşturmayı içerir. 3'ten fazla kenarı olan bir joker düğümün grafikteki kenar sayısını azaltması temelinde, G açısından azaltmak yerine bu grafiği kullanmayı düşündüm ve sanırım bir golf açısından yapmalıydım. standart en kötü durum çalışma süresi en kısa yol algoritmalarıdır O(V heap-op + E)
.
Ancak, yaptığım ilk şey, farklı kelime uzunlukları için G grafiklerinin bazı analizlerini yapmaktı ve 5 veya daha fazla harfli kelimeler için son derece seyrek olduklarını keşfettim. 5 harfli grafik 12478 köşeye ve 40759 kenara sahiptir; bağlantı düğümleri eklemek grafiği daha da kötüleştirir. 8 harfe kadar olan zamana kadar düğümlerden daha az kenar vardır ve kelimelerin 3 / 7'si "uzak" dır. Bu yüzden optimizasyon fikrini gerçekten yararlı bulmadım.
Yararlı olan fikir, yığını incelemekti. Dürüst olmak gerekirse, geçmişte orta derecede egzotik yığınlar uyguladığımı söyleyebilirim, ancak hiçbiri bu kadar egzotik değil. A-yıldız kullanıyorum (C kullandığım öbek göz önüne alındığında hiçbir fayda sağlamadığı için) hedeften farklı harflerin açık sezgisel taramalarını kullanıyorum ve biraz analiz, herhangi bir zamanda 3'ten fazla farklı önceliğin olmadığını gösteriyor yığın. Önceliği (maliyet + sezgisel) olan bir düğümü patlattığımda ve komşularına baktığımda, düşündüğüm üç durum var: 1) komşunun maliyeti maliyet + 1; komşunun sezgiselliği sezgisel-1'dir (çünkü değiştirdiği harf "doğru" olur); 2) maliyet + 1 ve sezgisel + 0 (çünkü değiştirdiği harf "yanlış" dan "hala yanlış" a gider; 3) maliyet + 1 ve sezgisel + 1 (çünkü değiştirdiği harf "doğru" dan "yanlış" a gider). Bu yüzden komşuyu rahatlatırsam, aynı öncelik, öncelik + 1 veya öncelik + 2'ye ekleyeceğim. Sonuç olarak yığın için 3-elemanlı bağlantılı listeler dizisini kullanabilirsiniz.
Karma aramaların sabit olduğu varsayımım hakkında bir not eklemeliyim. Çok iyi diyebilirsiniz, ama karma hesaplamalar ne olacak? Cevap şu: Onları amorti ediyorum: java.lang.String
önbelleğe alıyor hashCode()
, bu yüzden hesaplama karmaları harcanan toplam süre O(V n^2)
(grafiği oluştururken).
Karmaşıklığı etkileyen başka bir değişiklik daha var, ancak bunun bir optimizasyon olup olmadığı sorusu, istatistiklerle ilgili varsayımlarınıza bağlıdır. (IMO "en iyi Big O çözümünü" bir kriter olarak koymak bir hatadır, çünkü basit bir nedenden dolayı en iyi karmaşıklık yoktur: tek bir değişken yoktur). Bu değişiklik grafik oluşturma adımını etkiler. Yukarıdaki kodda:
Map<String, Set<String>> wordsToLinks = new HashMap<String, Set<String>>();
Map<String, Set<String>> linksToWords = new HashMap<String, Set<String>>();
// Cost: O(Vn * (n + hash))
for (String word : words)
{
// Cost: O(n*(n + hash))
for (int i = 0; i < word.length(); i++)
{
// Cost: O(n + hash)
char[] ch = word.toCharArray();
ch[i] = '.';
String link = new String(ch).intern();
add(wordsToLinks, word, link);
add(linksToWords, link, word);
}
}
// Cost: O(V * n * hash + E * hash)
for (Map.Entry<String, Set<String>> from : wordsToLinks.entrySet()) {
String src = from.getKey();
wordsToWords.put(src, new HashSet<String>());
for (String link : from.getValue()) {
Set<String> to = linksToWords.get(link);
for (String snk : to) {
// Note: equality test is safe here. Cost is O(hash)
if (snk != src) add(wordsToWords, src, snk);
}
}
}
İşte O(V * n * (n + hash) + E * hash)
. Ancak O(V * n^2)
bölüm, her bağlantı için yeni bir n karakter dizesi oluşturmak ve ardından hash kodunu hesaplamaktan gelir. Bir yardımcı sınıfla bu önlenebilir:
private static class Link
{
private String str;
private int hash;
private int missingIdx;
public Link(String str, int hash, int missingIdx) {
this.str = str;
this.hash = hash;
this.missingIdx = missingIdx;
}
@Override
public int hashCode() { return hash; }
@Override
public boolean equals(Object obj) {
Link l = (Link)obj; // Unsafe, but I know the contexts where I'm using this class...
if (this == l) return true; // Essential
if (hash != l.hash || missingIdx != l.missingIdx) return false;
for (int i = 0; i < str.length(); i++) {
if (i != missingIdx && str.charAt(i) != l.str.charAt(i)) return false;
}
return true;
}
}
Sonra grafik üretiminin ilk yarısı
Map<String, Set<Link>> wordsToLinks = new HashMap<String, Set<Link>>();
Map<Link, Set<String>> linksToWords = new HashMap<Link, Set<String>>();
// Cost: O(V * n * hash)
for (String word : words)
{
// apidoc: The hash code for a String object is computed as
// s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
// Cost: O(n * hash)
int hashCode = word.hashCode();
int pow = 1;
for (int j = word.length() - 1; j >= 0; j--) {
Link link = new Link(word, hashCode - word.charAt(j) * pow, j);
add(wordsToLinks, word, link);
add(linksToWords, link, word);
pow *= 31;
}
}
Hashcode yapısını kullanarak içindeki bağlantıları oluşturabiliriz O(V * n)
. Bununla birlikte, bunun bir vuruntu etkisi vardır. Karma aramaların sürekli olduğu varsayımımın doğasında var olan şey, nesnelerin eşitlik için karşılaştırılmasının ucuz olduğu bir varsayımdır. Ancak, Link'in eşitlik testi O(n)
en kötü durumdadır. En kötü durum, farklı kelimelerden üretilen iki eşit bağlantı arasında bir karma çarpışmamız olduğunda - yani O(E)
grafik üretiminin ikinci yarısındaki zamanlar. Bunun dışında, eşit olmayan bağlantılar arasındaki karma bir çarpışma olayı haricinde iyiyiz. Bu yüzden işlem ettik O(V * n^2)
için O(E * n * hash)
. İstatistiklerle ilgili önceki noktama bakın.
HOUSE
içinGORGE
ben 2 ara sözler vardır farkında 2. gibi mantıklı mı yani bildirilmektedir ancak operasyonların # daha sezgisel olacaktır.