Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
2
Task/Word-ladder/00-META.yaml
Normal file
2
Task/Word-ladder/00-META.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
from: http://rosettacode.org/wiki/Word_ladder
|
||||
20
Task/Word-ladder/00-TASK.txt
Normal file
20
Task/Word-ladder/00-TASK.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
Yet another shortest path problem. Given two words of equal length the task is to transpose the first into the second.
|
||||
|
||||
Only one letter may be changed at a time and the change must result in a word in [http://wiki.puzzlers.org/pub/wordlists/unixdict.txt unixdict], the minimum number of intermediate words should be used.
|
||||
|
||||
Demonstrate the following:
|
||||
|
||||
A boy can be made into a man: boy -> bay -> ban -> man
|
||||
|
||||
With a little more difficulty a girl can be made into a lady: girl -> gill -> gall -> gale -> gaze -> laze -> lazy -> lady
|
||||
|
||||
A john can be made into a jane: john -> cohn -> conn -> cone -> cane -> jane
|
||||
|
||||
A child can not be turned into an adult.
|
||||
|
||||
Optional transpositions of your choice.
|
||||
|
||||
|
||||
{{Template:Strings}}
|
||||
<br><br>
|
||||
|
||||
52
Task/Word-ladder/11l/word-ladder.11l
Normal file
52
Task/Word-ladder/11l/word-ladder.11l
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
F isOneAway(word1, word2)
|
||||
V result = 0B
|
||||
L(i) 0 .< word1.len
|
||||
I word1[i] != word2[i]
|
||||
I result
|
||||
R 0B
|
||||
E
|
||||
result = 1B
|
||||
R result
|
||||
|
||||
DefaultDict[Int, [String]] words
|
||||
|
||||
L(word) File(‘unixdict.txt’).read().split("\n")
|
||||
words[word.len] [+]= word
|
||||
|
||||
F find_path(start, target)
|
||||
V lg = start.len
|
||||
assert(target.len == lg, ‘Source and destination must have same length.’)
|
||||
assert(start C :words[lg], ‘Source must exist in the dictionary.’)
|
||||
assert(target C :words[lg], ‘Destination must exist in the dictionary.’)
|
||||
|
||||
V currPaths = [[start]]
|
||||
V pool = copy(:words[lg])
|
||||
|
||||
L
|
||||
[[String]] newPaths
|
||||
[String] added
|
||||
L(candidate) pool
|
||||
L(path) currPaths
|
||||
I isOneAway(candidate, path.last)
|
||||
V newPath = path [+] [candidate]
|
||||
I candidate == target
|
||||
R newPath
|
||||
E
|
||||
newPaths.append(newPath)
|
||||
added.append(candidate)
|
||||
L.break
|
||||
|
||||
I newPaths.empty
|
||||
L.break
|
||||
currPaths = move(newPaths)
|
||||
L(w) added
|
||||
pool.remove(w)
|
||||
|
||||
R [String]()
|
||||
|
||||
L(start, target) [(‘boy’, ‘man’), (‘girl’, ‘lady’), (‘john’, ‘jane’), (‘child’, ‘adult’), (‘cat’, ‘dog’), (‘lead’, ‘gold’), (‘white’, ‘black’), (‘bubble’, ‘tickle’)]
|
||||
V path = find_path(start, target)
|
||||
I path.empty
|
||||
print(‘No path from "’start‘" to "’target‘".’)
|
||||
E
|
||||
print(path.join(‘ -> ’))
|
||||
179
Task/Word-ladder/ALGOL-68/word-ladder.alg
Normal file
179
Task/Word-ladder/ALGOL-68/word-ladder.alg
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
# quick implementation of a stack of INT.
|
||||
real program starts after it.
|
||||
#
|
||||
MODE STACK = STRUCT (INT top, FLEX[1:0]INT data, INT increment);
|
||||
|
||||
PROC makestack = (INT increment)STACK: (1, (), increment);
|
||||
|
||||
PROC pop = (REF STACK s)INT: ( top OF s -:= 1; (data OF s)[top OF s] );
|
||||
|
||||
PROC push = (REF STACK s, INT n)VOID:
|
||||
BEGIN
|
||||
IF top OF s > UPB data OF s THEN
|
||||
[ UPB data OF s + increment OF s ]INT tmp;
|
||||
tmp[1 : UPB data OF s] := data OF s;
|
||||
data OF s := tmp
|
||||
FI;
|
||||
(data OF s)[top OF s] := n;
|
||||
top OF s +:= 1
|
||||
END;
|
||||
|
||||
PROC empty = (REF STACK s)BOOL: top OF s <= 1;
|
||||
|
||||
PROC contents = (REF STACK s)[]INT: (data OF s)[:top OF s - 1];
|
||||
|
||||
# start solution #
|
||||
|
||||
[]STRING words = BEGIN # load dictionary file into array #
|
||||
FILE f;
|
||||
BOOL eof := FALSE;
|
||||
open(f, "unixdict.txt", stand in channel);
|
||||
on logical file end(f, (REF FILE f)BOOL: eof := TRUE);
|
||||
INT idx := 1;
|
||||
FLEX [1:0] STRING words;
|
||||
STRING word;
|
||||
WHILE NOT eof DO
|
||||
get(f, (word, newline));
|
||||
IF idx > UPB words THEN
|
||||
HEAP [1 : UPB words + 10000]STRING tmp;
|
||||
tmp[1 : UPB words] := words;
|
||||
words := tmp
|
||||
FI;
|
||||
words[idx] := word;
|
||||
idx +:= 1
|
||||
OD;
|
||||
words[1:idx-1]
|
||||
END;
|
||||
|
||||
INT nwords = UPB words;
|
||||
|
||||
INT max word length = (INT mwl := 0;
|
||||
FOR i TO UPB words DO
|
||||
IF mwl < UPB words[i] THEN mwl := UPB words[i] FI
|
||||
OD;
|
||||
mwl);
|
||||
|
||||
[nwords]FLEX[0]INT neighbors;
|
||||
|
||||
[max word length]BOOL precalculated by length;
|
||||
|
||||
FOR i TO UPB precalculated by length DO precalculated by length[i] := FALSE OD;
|
||||
|
||||
# precalculating neighbours takes time, but not doing it is even slower... #
|
||||
PROC precalculate neighbors = (INT word length)VOID:
|
||||
BEGIN
|
||||
[nwords]REF STACK stacks;
|
||||
FOR i TO UPB stacks DO stacks[i] := NIL OD;
|
||||
FOR i TO UPB words DO
|
||||
IF UPB words[i] = word length THEN
|
||||
IF REF STACK(stacks[i]) :=: NIL THEN stacks[i] := HEAP STACK := makestack(10) FI;
|
||||
FOR j FROM i + 1 TO UPB words DO
|
||||
IF UPB words[j] = word length THEN
|
||||
IF neighboring(words[i], words[j]) THEN
|
||||
push(stacks[i], j);
|
||||
IF REF STACK(stacks[j]) :=: NIL THEN stacks[j] := HEAP STACK := makestack(10) FI;
|
||||
push(stacks[j], i)
|
||||
FI
|
||||
FI
|
||||
OD
|
||||
FI
|
||||
OD;
|
||||
FOR i TO UPB neighbors DO
|
||||
IF REF STACK(stacks[i]) :/=: NIL THEN
|
||||
neighbors[i] := contents(stacks[i])
|
||||
FI
|
||||
OD;
|
||||
precalculated by length [word length] := TRUE
|
||||
END;
|
||||
|
||||
PROC neighboring = (STRING a, b)BOOL: # do a & b differ in just 1 char? #
|
||||
BEGIN
|
||||
INT diff := 0;
|
||||
FOR i TO UPB a DO IF a[i] /= b[i] THEN diff +:= 1 FI OD;
|
||||
diff = 1
|
||||
END;
|
||||
|
||||
PROC word ladder = (STRING from, STRING to)[]STRING:
|
||||
BEGIN
|
||||
IF UPB from /= UPB to THEN fail FI;
|
||||
INT word length = UPB from;
|
||||
IF word length < 1 OR word length > max word length THEN fail FI;
|
||||
IF from = to THEN fail FI;
|
||||
INT start := 0;
|
||||
INT destination := 0;
|
||||
FOR i TO UPB words DO
|
||||
IF UPB words[i] = word length THEN
|
||||
IF words[i] = from THEN start := i
|
||||
ELIF words[i] = to THEN destination := i
|
||||
FI
|
||||
FI
|
||||
OD;
|
||||
IF destination = 0 OR start = 0 THEN fail FI;
|
||||
IF NOT precalculated by length [word length] THEN
|
||||
precalculate neighbors(word length)
|
||||
FI;
|
||||
STACK stack := makestack(1000);
|
||||
[nwords]INT distance;
|
||||
[nwords]INT previous;
|
||||
FOR i TO nwords DO distance[i] := nwords+1; previous[i] := 0 OD;
|
||||
INT shortest := nwords+1;
|
||||
distance[start] := 0;
|
||||
push(stack, start);
|
||||
WHILE NOT empty(stack)
|
||||
DO
|
||||
INT curr := pop(stack);
|
||||
INT dist := distance[curr];
|
||||
IF dist < shortest - 1 THEN
|
||||
# find neighbors and add them to the stack #
|
||||
FOR i FROM UPB neighbors[curr] BY -1 TO 1 DO
|
||||
INT n = neighbors[curr][i];
|
||||
IF distance[n] > dist + 1 THEN
|
||||
distance[n] := dist + 1;
|
||||
previous[n] := curr;
|
||||
IF n = destination THEN
|
||||
shortest := dist + 1
|
||||
ELSE
|
||||
push(stack, n)
|
||||
FI
|
||||
FI
|
||||
OD;
|
||||
IF curr = destination THEN shortest := dist FI
|
||||
FI
|
||||
OD;
|
||||
INT length = distance[destination] + 1;
|
||||
IF length > nwords THEN fail FI;
|
||||
[length]STRING result;
|
||||
INT curr := destination;
|
||||
FOR i FROM length BY -1 TO 1
|
||||
DO
|
||||
result[i] := words[curr];
|
||||
curr := previous[curr]
|
||||
OD;
|
||||
result EXIT
|
||||
fail: LOC [0] STRING
|
||||
END;
|
||||
|
||||
[][]STRING pairs = (("boy", "man"), ("bed", "cot"),
|
||||
("old", "new"), ("dry", "wet"),
|
||||
|
||||
("girl", "lady"), ("john", "jane"),
|
||||
("lead", "gold"), ("poor", "rich"),
|
||||
("lamb", "stew"), ("kick", "goal"),
|
||||
("cold", "warm"), ("nude", "clad"),
|
||||
|
||||
("child", "adult"), ("white", "black"),
|
||||
("bread", "toast"), ("lager", "stout"),
|
||||
("bride", "groom"), ("table", "chair"),
|
||||
|
||||
("bubble", "tickle"));
|
||||
|
||||
FOR i TO UPB pairs
|
||||
DO
|
||||
STRING from = pairs[i][1], to = pairs[i][2];
|
||||
[]STRING ladder = word ladder(from, to);
|
||||
IF UPB ladder = 0
|
||||
THEN print(("No solution for """ + from + """ -> """ + to + """", newline))
|
||||
ELSE FOR j TO UPB ladder DO print(((j > 1 | "->" | ""), ladder[j])) OD;
|
||||
print(newline)
|
||||
FI
|
||||
OD
|
||||
92
Task/Word-ladder/C++/word-ladder.cpp
Normal file
92
Task/Word-ladder/C++/word-ladder.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using word_map = std::map<size_t, std::vector<std::string>>;
|
||||
|
||||
// Returns true if strings s1 and s2 differ by one character.
|
||||
bool one_away(const std::string& s1, const std::string& s2) {
|
||||
if (s1.size() != s2.size())
|
||||
return false;
|
||||
bool result = false;
|
||||
for (size_t i = 0, n = s1.size(); i != n; ++i) {
|
||||
if (s1[i] != s2[i]) {
|
||||
if (result)
|
||||
return false;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Join a sequence of strings into a single string using the given separator.
|
||||
template <typename iterator_type, typename separator_type>
|
||||
std::string join(iterator_type begin, iterator_type end,
|
||||
separator_type separator) {
|
||||
std::string result;
|
||||
if (begin != end) {
|
||||
result += *begin++;
|
||||
for (; begin != end; ++begin) {
|
||||
result += separator;
|
||||
result += *begin;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// If possible, print the shortest chain of single-character modifications that
|
||||
// leads from "from" to "to", with each intermediate step being a valid word.
|
||||
// This is an application of breadth-first search.
|
||||
bool word_ladder(const word_map& words, const std::string& from,
|
||||
const std::string& to) {
|
||||
auto w = words.find(from.size());
|
||||
if (w != words.end()) {
|
||||
auto poss = w->second;
|
||||
std::vector<std::vector<std::string>> queue{{from}};
|
||||
while (!queue.empty()) {
|
||||
auto curr = queue.front();
|
||||
queue.erase(queue.begin());
|
||||
for (auto i = poss.begin(); i != poss.end();) {
|
||||
if (!one_away(*i, curr.back())) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (to == *i) {
|
||||
curr.push_back(to);
|
||||
std::cout << join(curr.begin(), curr.end(), " -> ") << '\n';
|
||||
return true;
|
||||
}
|
||||
std::vector<std::string> temp(curr);
|
||||
temp.push_back(*i);
|
||||
queue.push_back(std::move(temp));
|
||||
i = poss.erase(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << from << " into " << to << " cannot be done.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
int main() {
|
||||
word_map words;
|
||||
std::ifstream in("unixdict.txt");
|
||||
if (!in) {
|
||||
std::cerr << "Cannot open file unixdict.txt.\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::string word;
|
||||
while (getline(in, word))
|
||||
words[word.size()].push_back(word);
|
||||
word_ladder(words, "boy", "man");
|
||||
word_ladder(words, "girl", "lady");
|
||||
word_ladder(words, "john", "jane");
|
||||
word_ladder(words, "child", "adult");
|
||||
word_ladder(words, "cat", "dog");
|
||||
word_ladder(words, "lead", "gold");
|
||||
word_ladder(words, "white", "black");
|
||||
word_ladder(words, "bubble", "tickle");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
8
Task/Word-ladder/F-Sharp/word-ladder-1.fs
Normal file
8
Task/Word-ladder/F-Sharp/word-ladder-1.fs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Word ladder: Nigel Galloway. June 5th., 2021
|
||||
let fG n g=n|>List.partition(fun n->2>Seq.fold2(fun z n g->z+if n=g then 0 else 1) 0 n g)
|
||||
let wL n g=let dict=seq{use n=System.IO.File.OpenText("unixdict.txt") in while not n.EndOfStream do yield n.ReadLine()}|>Seq.filter(Seq.length>>(=)(Seq.length n))|>List.ofSeq|>List.except [n]
|
||||
let (|Done|_|) n=n|>List.tryFind((=)g)
|
||||
let rec wL n g l=match n with h::t->let i,e=fG l (List.head h) in match i with Done i->Some((i::h)|>List.rev) |_->wL t ((i|>List.map(fun i->i::h))@g) e
|
||||
|_->match g with []->None |_->wL g [] l
|
||||
let i,e=fG dict n in match i with Done i->Some([n;g]) |_->wL(i|>List.map(fun g->[g;n])) [] e
|
||||
[("boy","man");("girl","lady");("john","jane");("child","adult")]|>List.iter(fun(n,g)->printfn "%s" (match wL n g with Some n->n|>String.concat " -> " |_->n+" into "+g+" can't be done"))
|
||||
1
Task/Word-ladder/F-Sharp/word-ladder-2.fs
Normal file
1
Task/Word-ladder/F-Sharp/word-ladder-2.fs
Normal file
|
|
@ -0,0 +1 @@
|
|||
[("evil","good");("god","man")]|>List.iter(fun(n,g)->printfn "%s" (match wL n g with Some n->n|>String.concat " -> " |_->n+" into "+g+" can't be done"))
|
||||
89
Task/Word-ladder/Go/word-ladder.go
Normal file
89
Task/Word-ladder/Go/word-ladder.go
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func contains(a []string, s string) bool {
|
||||
for _, e := range a {
|
||||
if e == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func oneAway(a, b string) bool {
|
||||
sum := 0
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
sum++
|
||||
}
|
||||
}
|
||||
return sum == 1
|
||||
}
|
||||
|
||||
func wordLadder(words []string, a, b string) {
|
||||
l := len(a)
|
||||
var poss []string
|
||||
for _, word := range words {
|
||||
if len(word) == l {
|
||||
poss = append(poss, word)
|
||||
}
|
||||
}
|
||||
todo := [][]string{{a}}
|
||||
for len(todo) > 0 {
|
||||
curr := todo[0]
|
||||
todo = todo[1:]
|
||||
var next []string
|
||||
for _, word := range poss {
|
||||
if oneAway(word, curr[len(curr)-1]) {
|
||||
next = append(next, word)
|
||||
}
|
||||
}
|
||||
if contains(next, b) {
|
||||
curr = append(curr, b)
|
||||
fmt.Println(strings.Join(curr, " -> "))
|
||||
return
|
||||
}
|
||||
for i := len(poss) - 1; i >= 0; i-- {
|
||||
if contains(next, poss[i]) {
|
||||
copy(poss[i:], poss[i+1:])
|
||||
poss[len(poss)-1] = ""
|
||||
poss = poss[:len(poss)-1]
|
||||
}
|
||||
}
|
||||
for _, s := range next {
|
||||
temp := make([]string, len(curr))
|
||||
copy(temp, curr)
|
||||
temp = append(temp, s)
|
||||
todo = append(todo, temp)
|
||||
}
|
||||
}
|
||||
fmt.Println(a, "into", b, "cannot be done.")
|
||||
}
|
||||
|
||||
func main() {
|
||||
b, err := ioutil.ReadFile("unixdict.txt")
|
||||
if err != nil {
|
||||
log.Fatal("Error reading file")
|
||||
}
|
||||
bwords := bytes.Fields(b)
|
||||
words := make([]string, len(bwords))
|
||||
for i, bword := range bwords {
|
||||
words[i] = string(bword)
|
||||
}
|
||||
pairs := [][]string{
|
||||
{"boy", "man"},
|
||||
{"girl", "lady"},
|
||||
{"john", "jane"},
|
||||
{"child", "adult"},
|
||||
}
|
||||
for _, pair := range pairs {
|
||||
wordLadder(words, pair[0], pair[1])
|
||||
}
|
||||
}
|
||||
47
Task/Word-ladder/Haskell/word-ladder-1.hs
Normal file
47
Task/Word-ladder/Haskell/word-ladder-1.hs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import System.IO (readFile)
|
||||
import Control.Monad (foldM)
|
||||
import Data.List (intercalate)
|
||||
import qualified Data.Set as S
|
||||
|
||||
distance :: String -> String -> Int
|
||||
distance s1 s2 = length $ filter not $ zipWith (==) s1 s2
|
||||
|
||||
wordLadders :: [String] -> String -> String -> [[String]]
|
||||
wordLadders dict start end
|
||||
| length start /= length end = []
|
||||
| otherwise = [wordSpace] >>= expandFrom start >>= shrinkFrom end
|
||||
where
|
||||
|
||||
wordSpace = S.fromList $ filter ((length start ==) . length) dict
|
||||
|
||||
expandFrom s = go [[s]]
|
||||
where
|
||||
go (h:t) d
|
||||
| S.null d || S.null f = []
|
||||
| end `S.member` f = [h:t]
|
||||
| otherwise = go (S.elems f:h:t) (d S.\\ f)
|
||||
where
|
||||
f = foldr (\w -> S.union (S.filter (oneStepAway w) d)) mempty h
|
||||
|
||||
shrinkFrom = scanM (filter . oneStepAway)
|
||||
|
||||
oneStepAway x = (1 ==) . distance x
|
||||
|
||||
scanM f x = fmap snd . foldM g (x,[x])
|
||||
where g (b, r) a = (\x -> (x, x:r)) <$> f b a
|
||||
|
||||
wordLadder :: [String] -> String -> String -> [String]
|
||||
wordLadder d s e = case wordLadders d s e of
|
||||
[] -> []
|
||||
h:_ -> h
|
||||
|
||||
showChain [] = putStrLn "No chain"
|
||||
showChain ch = putStrLn $ intercalate " -> " ch
|
||||
|
||||
main = do
|
||||
dict <- lines <$> readFile "unixdict.txt"
|
||||
showChain $ wordLadder dict "boy" "man"
|
||||
showChain $ wordLadder dict "girl" "lady"
|
||||
showChain $ wordLadder dict "john" "jane"
|
||||
showChain $ wordLadder dict "alien" "drool"
|
||||
showChain $ wordLadder dict "child" "adult"
|
||||
32
Task/Word-ladder/Haskell/word-ladder-2.hs
Normal file
32
Task/Word-ladder/Haskell/word-ladder-2.hs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
wordLadders2 :: String -> String -> [String] -> [[String]]
|
||||
wordLadders2 start end dict
|
||||
| length start /= length end = []
|
||||
| otherwise = pure wordSpace >>= expand start end >>= shrink end
|
||||
where
|
||||
|
||||
wordSpace = S.fromList $ filter ((length start ==) . length) dict
|
||||
|
||||
expand s e d = tail . map S.elems <$> go [S.singleton s] [S.singleton e] d
|
||||
where
|
||||
go (hs:ts) (he:te) d
|
||||
| S.null d || S.null fs || S.null fe = []
|
||||
| not $ S.null f1 = [reverse (f1:te) ++ hs:ts]
|
||||
| not $ S.null f2 = [reverse (he:te) ++ f2:ts]
|
||||
| not $ S.null f3 = [reverse (he:te) ++ f3:hs:ts]
|
||||
| otherwise = go (fs:hs:ts) (fe:he:te) (d S.\\ hs S.\\ he)
|
||||
where
|
||||
fs = front hs
|
||||
fe = front he
|
||||
f1 = fs `S.intersection` he
|
||||
f2 = fe `S.intersection` hs
|
||||
f3 = fs `S.intersection` fe
|
||||
front = S.foldr (\w -> S.union (S.filter (oneStepAway w) d)) mempty
|
||||
|
||||
shrink = scanM (findM . oneStepAway)
|
||||
|
||||
oneStepAway x = (1 ==) . distance x
|
||||
|
||||
scanM f x = fmap snd . foldM g (x,[x])
|
||||
where g (b, r) a = (\x -> (x, x:r)) <$> f b a
|
||||
|
||||
findM p = msum . map (\x -> if p x then pure x else mzero)
|
||||
13
Task/Word-ladder/Haskell/word-ladder-3.hs
Normal file
13
Task/Word-ladder/Haskell/word-ladder-3.hs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import AStar (findPath, Graph(..))
|
||||
import qualified Data.Map as M
|
||||
|
||||
distance :: String -> String -> Int
|
||||
distance s1 s2 = length $ filter not $ zipWith (==) s1 s2
|
||||
|
||||
wordLadder :: [String] -> String -> String -> [String]
|
||||
wordLadder dict start end = findPath g distance start end
|
||||
where
|
||||
short_dict = filter ((length start ==) . length) dict
|
||||
g = Graph $ \w -> M.fromList [ (x, 1)
|
||||
| x <- short_dict
|
||||
, distance w x == 1 ]
|
||||
30
Task/Word-ladder/J/word-ladder-1.j
Normal file
30
Task/Word-ladder/J/word-ladder-1.j
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
extend=: {{
|
||||
j=. {:y
|
||||
l=. <:{:$m
|
||||
<y,"1 0 I.l=m+/ .="1 j{m
|
||||
}}
|
||||
|
||||
wlad=: {{
|
||||
l=. #x assert. l=#y
|
||||
words=. >(#~ l=#@>) cutLF fread 'unixdict.txt'
|
||||
ix=. ,:words i.x assert. ix<#words
|
||||
iy=. ,:words i.y assert. iy<#words
|
||||
while. -. 1 e. ix e.&, iy do.
|
||||
if. 0 e. ix,&# iy do. EMPTY return. end.
|
||||
ix=. ; words extend"1 ix
|
||||
if. -. 1 e. ix e.&, iy do.
|
||||
iy=. ; words extend"1 iy
|
||||
end.
|
||||
end.
|
||||
iy=. |."1 iy
|
||||
r=. ix,&,iy
|
||||
for_jk.(ix,&#iy)#:I.,ix +./@e."1/ iy do.
|
||||
ixj=. ({.jk){ix
|
||||
iyk=. ({:jk){iy
|
||||
for_c. ixj ([-.-.) iyk do.
|
||||
path=. (ixj{.~ixj i.c) , iyk}.~ iyk i.c
|
||||
if. path <&# r do. r=. path end.
|
||||
end.
|
||||
end.
|
||||
}.,' ',.r{words
|
||||
}}
|
||||
15
Task/Word-ladder/J/word-ladder-2.j
Normal file
15
Task/Word-ladder/J/word-ladder-2.j
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
'boy' wlad 'man'
|
||||
boy bay ban man
|
||||
'girl' wlad 'lady'
|
||||
girl gill gall gale gaze laze lazy lady
|
||||
'john' wlad 'jane'
|
||||
john cohn conn cone cane jane
|
||||
'child' wlad 'adult'
|
||||
'cat' wlad 'dog'
|
||||
cat cot cog dog
|
||||
'lead' wlad 'gold'
|
||||
lead load goad gold
|
||||
'white' wlad 'black'
|
||||
white whine chine chink clink blink blank black
|
||||
'bubble' wlad 'tickle'
|
||||
bubble babble gabble garble gargle gaggle giggle jiggle jingle tingle tinkle tickle
|
||||
94
Task/Word-ladder/Java/word-ladder-1.java
Normal file
94
Task/Word-ladder/Java/word-ladder-1.java
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Set;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class WordLadder {
|
||||
private static int distance(String s1, String s2) {
|
||||
assert s1.length() == s2.length();
|
||||
return (int) IntStream.range(0, s1.length())
|
||||
.filter(i -> s1.charAt(i) != s2.charAt(i))
|
||||
.count();
|
||||
}
|
||||
|
||||
private static void wordLadder(Map<Integer, Set<String>> words, String fw, String tw) {
|
||||
wordLadder(words, fw, tw, 8);
|
||||
}
|
||||
|
||||
private static void wordLadder(Map<Integer, Set<String>> words, String fw, String tw, int limit) {
|
||||
if (fw.length() != tw.length()) {
|
||||
throw new IllegalArgumentException("From word and to word must have the same length");
|
||||
}
|
||||
|
||||
Set<String> ws = words.get(fw.length());
|
||||
if (ws.contains(fw)) {
|
||||
List<String> primeList = new ArrayList<>();
|
||||
primeList.add(fw);
|
||||
|
||||
PriorityQueue<List<String>> queue = new PriorityQueue<>((chain1, chain2) -> {
|
||||
int cmp1 = Integer.compare(chain1.size(), chain2.size());
|
||||
if (cmp1 == 0) {
|
||||
String last1 = chain1.get(chain1.size() - 1);
|
||||
int d1 = distance(last1, tw);
|
||||
|
||||
String last2 = chain2.get(chain2.size() - 1);
|
||||
int d2 = distance(last2, tw);
|
||||
|
||||
return Integer.compare(d1, d2);
|
||||
}
|
||||
return cmp1;
|
||||
});
|
||||
queue.add(primeList);
|
||||
|
||||
while (queue.size() > 0) {
|
||||
List<String> curr = queue.remove();
|
||||
if (curr.size() > limit) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String last = curr.get(curr.size() - 1);
|
||||
for (String word : ws) {
|
||||
if (distance(last, word) == 1) {
|
||||
if (word.equals(tw)) {
|
||||
curr.add(word);
|
||||
System.out.println(String.join(" -> ", curr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!curr.contains(word)) {
|
||||
List<String> cp = new ArrayList<>(curr);
|
||||
cp.add(word);
|
||||
queue.add(cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.err.printf("Cannot turn `%s` into `%s`%n", fw, tw);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
Map<Integer, Set<String>> words = new HashMap<>();
|
||||
for (String line : Files.readAllLines(Path.of("unixdict.txt"))) {
|
||||
Set<String> wl = words.computeIfAbsent(line.length(), HashSet::new);
|
||||
wl.add(line);
|
||||
}
|
||||
|
||||
wordLadder(words, "boy", "man");
|
||||
wordLadder(words, "girl", "lady");
|
||||
wordLadder(words, "john", "jane");
|
||||
wordLadder(words, "child", "adult");
|
||||
wordLadder(words, "cat", "dog");
|
||||
wordLadder(words, "lead", "gold");
|
||||
wordLadder(words, "white", "black");
|
||||
wordLadder(words, "bubble", "tickle", 12);
|
||||
}
|
||||
}
|
||||
72
Task/Word-ladder/Java/word-ladder-2.java
Normal file
72
Task/Word-ladder/Java/word-ladder-2.java
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
public class WordLadder {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
Map<Integer, List<String>> words = new HashMap<>();
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader("unixdict.txt"))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null)
|
||||
words.computeIfAbsent(line.length(), k -> new ArrayList<String>()).add(line);
|
||||
}
|
||||
wordLadder(words, "boy", "man");
|
||||
wordLadder(words, "girl", "lady");
|
||||
wordLadder(words, "john", "jane");
|
||||
wordLadder(words, "child", "adult");
|
||||
wordLadder(words, "cat", "dog");
|
||||
wordLadder(words, "lead", "gold");
|
||||
wordLadder(words, "white", "black");
|
||||
wordLadder(words, "bubble", "tickle");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if strings s1 and s2 differ by one character.
|
||||
private static boolean oneAway(String s1, String s2) {
|
||||
if (s1.length() != s2.length())
|
||||
return false;
|
||||
boolean result = false;
|
||||
for (int i = 0, n = s1.length(); i != n; ++i) {
|
||||
if (s1.charAt(i) != s2.charAt(i)) {
|
||||
if (result)
|
||||
return false;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// If possible, print the shortest chain of single-character modifications that
|
||||
// leads from "from" to "to", with each intermediate step being a valid word.
|
||||
// This is an application of breadth-first search.
|
||||
private static void wordLadder(Map<Integer, List<String>> words, String from, String to) {
|
||||
List<String> w = words.get(from.length());
|
||||
if (w != null) {
|
||||
Deque<String> poss = new ArrayDeque<>(w);
|
||||
Deque<String> f = new ArrayDeque<String>();
|
||||
f.add(from);
|
||||
Deque<Deque<String>> queue = new ArrayDeque<>();
|
||||
queue.add(f);
|
||||
while (!queue.isEmpty()) {
|
||||
Deque<String> curr = queue.poll();
|
||||
for (Iterator<String> i = poss.iterator(); i.hasNext(); ) {
|
||||
String str = i.next();
|
||||
if (!oneAway(str, curr.getLast()))
|
||||
continue;
|
||||
if (to.equals(str)) {
|
||||
curr.add(to);
|
||||
System.out.println(String.join(" -> ", curr));
|
||||
return;
|
||||
}
|
||||
Deque<String> temp = new ArrayDeque<>(curr);
|
||||
temp.add(str);
|
||||
queue.add(temp);
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.printf("%s into %s cannot be done.\n", from, to);
|
||||
}
|
||||
}
|
||||
43
Task/Word-ladder/Jq/word-ladder.jq
Normal file
43
Task/Word-ladder/Jq/word-ladder.jq
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
def count(stream): reduce stream as $i (0; .+1);
|
||||
|
||||
def words: [inputs]; # one way to read the word list
|
||||
|
||||
def oneAway($a; $b):
|
||||
($a|explode) as $ax
|
||||
| ($b|explode) as $bx
|
||||
| 1 == count(range(0; $a|length) | select($ax[.] != $bx[.]));
|
||||
|
||||
# input: the word list
|
||||
def wordLadder($a; $b):
|
||||
($a|length) as $len
|
||||
| { poss: map(select(length == $len)), # the relevant words
|
||||
todo: [[$a]] # possible chains
|
||||
}
|
||||
| until ( ((.todo|length) == 0) or .solution;
|
||||
.curr = .todo[0]
|
||||
| .todo |= .[1:]
|
||||
| .curr[-1] as $c
|
||||
| (.poss | map(select( oneAway(.; $c) ))) as $next
|
||||
| if ($b | IN($next[]))
|
||||
then .curr += [$b]
|
||||
| .solution = (.curr|join(" -> "))
|
||||
else .poss = (.poss - $next)
|
||||
| .curr as $curr
|
||||
| .todo = (reduce range(0; $next|length) as $i (.todo;
|
||||
. + [$curr + [$next[$i] ]] ))
|
||||
end )
|
||||
| if .solution then .solution
|
||||
else "There is no ladder from \($a) to \($b)."
|
||||
end ;
|
||||
|
||||
def pairs:
|
||||
["boy", "man"],
|
||||
["girl", "lady"],
|
||||
["john", "jane"],
|
||||
["child", "adult"],
|
||||
["word", "play"]
|
||||
;
|
||||
|
||||
words
|
||||
| pairs as $p
|
||||
| wordLadder($p[0]; $p[1])
|
||||
26
Task/Word-ladder/Julia/word-ladder.julia
Normal file
26
Task/Word-ladder/Julia/word-ladder.julia
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
const dict = Set(split(read("unixdict.txt", String), r"\s+"))
|
||||
|
||||
function targeted_mutations(str::AbstractString, target::AbstractString)
|
||||
working, tried = [[str]], Set{String}()
|
||||
while all(a -> a[end] != target, working)
|
||||
newworking = Vector{Vector{String}}()
|
||||
for arr in working
|
||||
s = arr[end]
|
||||
push!(tried, s)
|
||||
for j in 1:length(s), c in 'a':'z'
|
||||
w = s[1:j-1] * c * s[j+1:end]
|
||||
if w in dict && !(w in tried)
|
||||
push!(newworking, [arr; w])
|
||||
end
|
||||
end
|
||||
end
|
||||
isempty(newworking) && return [["This cannot be done."]]
|
||||
working = newworking
|
||||
end
|
||||
return filter(a -> a[end] == target, working)
|
||||
end
|
||||
|
||||
println("boy to man: ", targeted_mutations("boy", "man"))
|
||||
println("girl to lady: ", targeted_mutations("girl", "lady"))
|
||||
println("john to jane: ", targeted_mutations("john", "jane"))
|
||||
println("child to adult: ", targeted_mutations("child", "adult"))
|
||||
15
Task/Word-ladder/Mathematica/word-ladder.math
Normal file
15
Task/Word-ladder/Mathematica/word-ladder.math
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
db=DeleteDuplicates[RemoveDiacritics[ToLowerCase[Select[DictionaryLookup[],StringLength/*EqualTo[3]]]]];
|
||||
sel=Select[Subsets[db,{2}],HammingDistance[#[[1]],#[[2]]]==1&];
|
||||
g=Graph[db,UndirectedEdge@@@sel];
|
||||
FindShortestPath[g,"boy","man"]
|
||||
|
||||
db=DeleteDuplicates[RemoveDiacritics[ToLowerCase[Select[DictionaryLookup[],StringLength/*EqualTo[4]]]]];
|
||||
sel=Select[Subsets[db,{2}],HammingDistance[#[[1]],#[[2]]]==1&];
|
||||
g=Graph[db,UndirectedEdge@@@sel];
|
||||
FindShortestPath[g,"girl","lady"]
|
||||
FindShortestPath[g,"john","jane"]
|
||||
|
||||
db=DeleteDuplicates[RemoveDiacritics[ToLowerCase[Select[DictionaryLookup[],StringLength/*EqualTo[5]]]]];
|
||||
sel=Select[Subsets[db,{2}],HammingDistance[#[[1]],#[[2]]]==1&];
|
||||
g=Graph[db,UndirectedEdge@@@sel];
|
||||
FindShortestPath[g,"child","adult"]
|
||||
56
Task/Word-ladder/Nim/word-ladder.nim
Normal file
56
Task/Word-ladder/Nim/word-ladder.nim
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import sets, strformat, strutils
|
||||
|
||||
|
||||
func isOneAway(word1, word2: string): bool =
|
||||
## Return true if "word1" and "word2" has only one letter of difference.
|
||||
for i in 0..word1.high:
|
||||
if word1[i] != word2[i]:
|
||||
if result: return false # More than one letter of difference.
|
||||
else: result = true # One letter of difference, for now.
|
||||
|
||||
var words: array[1..22, HashSet[string]] # Set of words sorted by length.
|
||||
|
||||
for word in "unixdict.txt".lines:
|
||||
words[word.len].incl word
|
||||
|
||||
|
||||
proc path(start, target: string): seq[string] =
|
||||
## Return a path from "start" to "target" or an empty list
|
||||
## if there is no possible path.
|
||||
let lg = start.len
|
||||
doAssert target.len == lg, "Source and destination must have same length."
|
||||
doAssert start in words[lg], "Source must exist in the dictionary."
|
||||
doAssert target in words[lg], "Destination must exist in the dictionary."
|
||||
|
||||
var currPaths = @[@[start]] # Current list of paths found.
|
||||
var pool = words[lg] # List of possible words to use.
|
||||
|
||||
while true:
|
||||
var newPaths: seq[seq[string]] # Next list of paths.
|
||||
var added: HashSet[string] # Set of words added during the round.
|
||||
for candidate in pool:
|
||||
for path in currPaths:
|
||||
if candidate.isOneAway(path[^1]):
|
||||
let newPath = path & candidate
|
||||
if candidate == target:
|
||||
# Found a path.
|
||||
return newPath
|
||||
else:
|
||||
# Not the target. Add a new path.
|
||||
newPaths.add newPath
|
||||
added.incl candidate
|
||||
break
|
||||
if newPaths.len == 0: break # No path.
|
||||
currPaths = move(newPaths) # Update list of paths.
|
||||
pool.excl added # Remove added words from pool.
|
||||
|
||||
|
||||
when isMainModule:
|
||||
for (start, target) in [("boy", "man"), ("girl", "lady"), ("john", "jane"),
|
||||
("child", "adult"), ("cat", "dog"), ("lead", "gold"),
|
||||
("white", "black"), ("bubble", "tickle")]:
|
||||
let path = path(start, target)
|
||||
if path.len == 0:
|
||||
echo &"No path from “{start}” to “{target}”."
|
||||
else:
|
||||
echo path.join(" → ")
|
||||
99
Task/Word-ladder/Perl/word-ladder-1.pl
Normal file
99
Task/Word-ladder/Perl/word-ladder-1.pl
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
my %dict;
|
||||
|
||||
open my $handle, '<', 'unixdict.txt';
|
||||
while (my $word = <$handle>) {
|
||||
chomp($word);
|
||||
my $len = length $word;
|
||||
if (exists $dict{$len}) {
|
||||
push @{ $dict{ $len } }, $word;
|
||||
} else {
|
||||
my @words = ( $word );
|
||||
$dict{$len} = \@words;
|
||||
}
|
||||
}
|
||||
close $handle;
|
||||
|
||||
sub distance {
|
||||
my $w1 = shift;
|
||||
my $w2 = shift;
|
||||
|
||||
my $dist = 0;
|
||||
for my $i (0 .. length($w1) - 1) {
|
||||
my $c1 = substr($w1, $i, 1);
|
||||
my $c2 = substr($w2, $i, 1);
|
||||
if (not ($c1 eq $c2)) {
|
||||
$dist++;
|
||||
}
|
||||
}
|
||||
return $dist;
|
||||
}
|
||||
|
||||
sub contains {
|
||||
my $aref = shift;
|
||||
my $needle = shift;
|
||||
|
||||
for my $v (@$aref) {
|
||||
if ($v eq $needle) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub word_ladder {
|
||||
my $fw = shift;
|
||||
my $tw = shift;
|
||||
|
||||
if (exists $dict{length $fw}) {
|
||||
my @poss = @{ $dict{length $fw} };
|
||||
my @queue = ([$fw]);
|
||||
while (scalar @queue > 0) {
|
||||
my $curr_ref = shift @queue;
|
||||
my $last = $curr_ref->[-1];
|
||||
|
||||
my @next;
|
||||
for my $word (@poss) {
|
||||
if (distance($last, $word) == 1) {
|
||||
push @next, $word;
|
||||
}
|
||||
}
|
||||
|
||||
if (contains(\@next, $tw)) {
|
||||
push @$curr_ref, $tw;
|
||||
print join (' -> ', @$curr_ref), "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
for my $word (@next) {
|
||||
for my $i (0 .. scalar @poss - 1) {
|
||||
if ($word eq $poss[$i]) {
|
||||
splice @poss, $i, 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for my $word (@next) {
|
||||
my @temp = @$curr_ref;
|
||||
push @temp, $word;
|
||||
|
||||
push @queue, \@temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print STDERR "Cannot change $fw into $tw\n";
|
||||
}
|
||||
|
||||
word_ladder('boy', 'man');
|
||||
word_ladder('girl', 'lady');
|
||||
word_ladder('john', 'jane');
|
||||
word_ladder('child', 'adult');
|
||||
word_ladder('cat', 'dog');
|
||||
word_ladder('lead', 'gold');
|
||||
word_ladder('white', 'black');
|
||||
word_ladder('bubble', 'tickle');
|
||||
51
Task/Word-ladder/Perl/word-ladder-2.pl
Normal file
51
Task/Word-ladder/Perl/word-ladder-2.pl
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
use feature 'say';
|
||||
|
||||
my %dict;
|
||||
open my $handle, '<', 'ref/unixdict.txt';
|
||||
while (my $word = <$handle>) {
|
||||
chomp $word;
|
||||
my $l = length $word;
|
||||
if ($dict{$l}) { push @{ $dict{$l} }, $word }
|
||||
else { $dict{$l} = \@{[$word]} }
|
||||
}
|
||||
close $handle;
|
||||
|
||||
sub distance {
|
||||
my($w1,$w2) = @_;
|
||||
my $d;
|
||||
substr($w1, $_, 1) eq substr($w2, $_, 1) or $d++ for 0 .. length($w1) - 1;
|
||||
return $d // 0;
|
||||
}
|
||||
|
||||
sub contains {
|
||||
my($aref,$needle) = @_;
|
||||
$needle eq $_ and return 1 for @$aref;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub word_ladder {
|
||||
my($fw,$tw) = @_;
|
||||
say 'Nothing like that in dictionary.' and return unless $dict{length $fw};
|
||||
|
||||
my @poss = @{ $dict{length $fw} };
|
||||
my @queue = [$fw];
|
||||
while (@queue) {
|
||||
my $curr_ref = shift @queue;
|
||||
my $last = $curr_ref->[-1];
|
||||
|
||||
my @next;
|
||||
distance($last, $_) == 1 and push @next, $_ for @poss;
|
||||
push(@$curr_ref, $tw) and say join ' -> ', @$curr_ref and return if contains \@next, $tw;
|
||||
|
||||
for my $word (@next) {
|
||||
$word eq $poss[$_] and splice(@poss, $_, 1) and last for 0 .. @poss - 1;
|
||||
}
|
||||
push @queue, \@{[@{$curr_ref}, $_]} for @next;
|
||||
}
|
||||
|
||||
say "Cannot change $fw into $tw";
|
||||
}
|
||||
|
||||
word_ladder(split) for 'boy man', 'girl lady', 'john jane', 'child adult';
|
||||
31
Task/Word-ladder/Phix/word-ladder.phix
Normal file
31
Task/Word-ladder/Phix/word-ladder.phix
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
(phixonline)-->
|
||||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
|
||||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">words</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">unix_dict</span><span style="color: #0000FF;">()</span>
|
||||
|
||||
<span style="color: #008080;">function</span> <span style="color: #000000;">right_length</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">word</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">l</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">return</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">word</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">l</span> <span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
|
||||
|
||||
<span style="color: #008080;">function</span> <span style="color: #000000;">one_away</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">a</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">b</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">return</span> <span style="color: #7060A8;">sum</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sq_ne</span><span style="color: #0000FF;">(</span><span style="color: #000000;">a</span><span style="color: #0000FF;">,</span><span style="color: #000000;">b</span><span style="color: #0000FF;">))=</span><span style="color: #000000;">1</span> <span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
|
||||
|
||||
<span style="color: #008080;">function</span> <span style="color: #000000;">dca</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">return</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">),</span><span style="color: #000000;">n</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
|
||||
|
||||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">word_ladder</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">a</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">b</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">poss</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #000000;">words</span><span style="color: #0000FF;">,</span><span style="color: #000000;">right_length</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">a</span><span style="color: #0000FF;">)),</span>
|
||||
<span style="color: #000000;">todo</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">a</span><span style="color: #0000FF;">}},</span>
|
||||
<span style="color: #000000;">curr</span> <span style="color: #000080;font-style:italic;">-- aka todo[1], word chain starting from a</span>
|
||||
<span style="color: #008080;">while</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">todo</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #000000;">curr</span><span style="color: #0000FF;">,</span><span style="color: #000000;">todo</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">todo</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">],</span><span style="color: #000000;">todo</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">..$]}</span>
|
||||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">next</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #000000;">poss</span><span style="color: #0000FF;">,</span><span style="color: #000000;">one_away</span><span style="color: #0000FF;">,</span><span style="color: #000000;">curr</span><span style="color: #0000FF;">[$])</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">b</span><span style="color: #0000FF;">,</span><span style="color: #000000;">next</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">curr</span><span style="color: #0000FF;">),</span><span style="color: #000000;">b</span><span style="color: #0000FF;">),</span><span style="color: #008000;">"->"</span><span style="color: #0000FF;">)})</span>
|
||||
<span style="color: #008080;">return</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #000000;">poss</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #000000;">poss</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"out"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">next</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #000000;">todo</span> <span style="color: #0000FF;">&=</span> <span style="color: #7060A8;">apply</span><span style="color: #0000FF;">(</span><span style="color: #004600;">true</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dca</span><span style="color: #0000FF;">,{{</span><span style="color: #000000;">curr</span><span style="color: #0000FF;">},</span><span style="color: #000000;">next</span><span style="color: #0000FF;">})</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
|
||||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s into %s cannot be done\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">a</span><span style="color: #0000FF;">,</span><span style="color: #000000;">b</span><span style="color: #0000FF;">})</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
|
||||
<span style="color: #000000;">word_ladder</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"boy"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"man"</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #000000;">word_ladder</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"girl"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"lady"</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #000000;">word_ladder</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"john"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"jane"</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #000000;">word_ladder</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"child"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"adult"</span><span style="color: #0000FF;">)</span>
|
||||
<!--
|
||||
48
Task/Word-ladder/Python/word-ladder.py
Normal file
48
Task/Word-ladder/Python/word-ladder.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import os,sys,zlib,urllib.request
|
||||
|
||||
def h ( str,x=9 ):
|
||||
for c in str :
|
||||
x = ( x*33 + ord( c )) & 0xffffffffff
|
||||
return x
|
||||
|
||||
def cache ( func,*param ):
|
||||
n = 'cache_%x.bin'%abs( h( repr( param )))
|
||||
try : return eval( zlib.decompress( open( n,'rb' ).read()))
|
||||
except : pass
|
||||
s = func( *param )
|
||||
open( n,'wb' ).write( zlib.compress( bytes( repr( s ),'ascii' )))
|
||||
return s
|
||||
|
||||
dico_url = 'https://raw.githubusercontent.com/quinnj/Rosetta-Julia/master/unixdict.txt'
|
||||
read_url = lambda url : urllib.request.urlopen( url ).read()
|
||||
load_dico = lambda url : tuple( cache( read_url,url ).split( b'\n'))
|
||||
isnext = lambda w1,w2 : len( w1 ) == len( w2 ) and len( list( filter( lambda l : l[0]!=l[1] , zip( w1,w2 )))) == 1
|
||||
|
||||
def build_map ( words ):
|
||||
map = [(w.decode('ascii'),[]) for w in words]
|
||||
for i1,(w1,n1) in enumerate( map ):
|
||||
for i2,(w2,n2) in enumerate( map[i1+1:],i1+1 ):
|
||||
if isnext( w1,w2 ):
|
||||
n1.append( i2 )
|
||||
n2.append( i1 )
|
||||
return map
|
||||
|
||||
def find_path ( words,w1,w2 ):
|
||||
i = [w[0] for w in words].index( w1 )
|
||||
front,done,res = [i],{i:-1},[]
|
||||
while front :
|
||||
i = front.pop(0)
|
||||
word,next = words[i]
|
||||
for n in next :
|
||||
if n in done : continue
|
||||
done[n] = i
|
||||
if words[n][0] == w2 :
|
||||
while n >= 0 :
|
||||
res = [words[n][0]] + res
|
||||
n = done[n]
|
||||
return ' '.join( res )
|
||||
front.append( n )
|
||||
return '%s can not be turned into %s'%( w1,w2 )
|
||||
|
||||
for w in ('boy man','girl lady','john jane','alien drool','child adult'):
|
||||
print( find_path( cache( build_map,load_dico( dico_url )),*w.split()))
|
||||
2
Task/Word-ladder/REXX/word-ladder-1.rexx
Normal file
2
Task/Word-ladder/REXX/word-ladder-1.rexx
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
lower: procedure; parse arg a; @= 'abcdefghijklmnopqrstuvwxyz'; @u= @; upper @u
|
||||
return translate(a, @, @u)
|
||||
57
Task/Word-ladder/REXX/word-ladder-2.rexx
Normal file
57
Task/Word-ladder/REXX/word-ladder-2.rexx
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*REXX program finds words (within an identified dict.) to solve a word ladder puzzle.*/
|
||||
parse arg base targ iFID . /*obtain optional arguments from the CL*/
|
||||
if base=='' | base=="," then base= 'boy' /*Not specified? Then use the default.*/
|
||||
if targ=='' | targ=="," then targ= 'man' /* " " " " " " */
|
||||
if iFID=='' | iFID=="," then iFID='unixdict.txt' /* " " " " " " */
|
||||
abc= 'abcdefghijklmnopqrstuvwxyz' /*the lowercase (Latin) alphabet. */
|
||||
abcU= abc; upper abcU /* " uppercase " " */
|
||||
base= lower(base); targ= lower(targ) /*lowercase the BASE and also the TARG.*/
|
||||
L= length(base) /*length of the BASE (in characters). */
|
||||
if L<2 then call err 'base word is too small or missing' /*oops, too small*/
|
||||
if length(targ)\==L then call msg , "target word isn't the same length as the base word"
|
||||
call letters /*assign letters, faster than SUBSTR. */
|
||||
#= 0 /*# of words whose length matches BASE.*/
|
||||
@.= /*default value of any dictionary word.*/
|
||||
do recs=0 while lines(iFID)\==0 /*read each word in the file (word=X).*/
|
||||
x= lower(strip( linein( iFID) ) ) /*pick off a word from the input line. */
|
||||
if length(x)\==L then iterate /*Word not correct length? Then skip. */
|
||||
#= # + 1; @.x= 1 /*bump # words with length L; semaphore*/
|
||||
end /*recs*/ /* [↑] semaphore name is uppercased. */
|
||||
!.= 0
|
||||
say copies('─', 30) recs "words in the dictionary file: " iFID
|
||||
say copies('─', 30) # "words in the dictionary file of length: " L
|
||||
say copies('─', 30) ' base word is: ' base
|
||||
say copies('─', 30) 'target word is: ' targ
|
||||
rung= targ
|
||||
$= base
|
||||
do f=1 for m; call look; if result\=='' then leave /*Found? Quit.*/
|
||||
end /*f*/
|
||||
say
|
||||
if f>m then call msg 'no word ladder solution possible for ' base " ──► " targ
|
||||
|
||||
do f-2; $= base; !.= 0 /*process all the rungs that were found*/
|
||||
do forever; call look; if result\=='' then leave /*Found? Quit.*/
|
||||
end /*forever*/
|
||||
end /*f-2*/
|
||||
call show words(rung)
|
||||
exit 0 /*stick a fork in it, we're all done. */
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
msg: say; if arg()==2 then say '***error*** ' arg(2); else say arg(1); say; exit 13
|
||||
show: say 'a solution: ' base; do j=1 to arg(1); say left('',12) word(rung,j); end; return
|
||||
letters: do m=1 for length(abc); a.m= substr(abc, m, 1); end; return
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
look: procedure expose @. !. a. $ abc base L rung targ search; rungs= word(rung, 1)
|
||||
$$=; rung#= words(rungs)
|
||||
do i=1 for words($); y= word($, i); !.y= 1
|
||||
do k=1 for L
|
||||
do n=1 for 26; z= overlay(a.n, y, k) /*change a letter*/
|
||||
if @.z=='' then iterate /*Is this not a word? Then skip it. */
|
||||
if !.z then iterate /* " " a repeat? " " " */
|
||||
if z==rungs then rung= y rung /*prepend a word to the rung list. */
|
||||
if z==rungs & rung#>1 then return z /*short─circuit. */
|
||||
if z==targ then return z
|
||||
$$= $$ z /*append a possible ladder word to $$*/
|
||||
end /*n*/
|
||||
end /*k*/
|
||||
end /*i*/
|
||||
$= $$; return ''
|
||||
41
Task/Word-ladder/Racket/word-ladder.rkt
Normal file
41
Task/Word-ladder/Racket/word-ladder.rkt
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#lang racket
|
||||
|
||||
(define *unixdict* (delay (with-input-from-file "../../data/unixdict.txt"
|
||||
(compose list->set port->lines))))
|
||||
|
||||
(define letters-as-strings (map string (string->list "abcdefghijklmnopqrstuvwxyz")))
|
||||
|
||||
(define ((replace-for-c-at-i w i) c)
|
||||
(string-append (substring w 0 i) c (substring w (add1 i))))
|
||||
|
||||
(define (candidates w)
|
||||
(for*/list (((i w_i) (in-parallel (string-length w) w))
|
||||
(r (in-value (replace-for-c-at-i w i)))
|
||||
(c letters-as-strings)
|
||||
#:unless (char=? w_i (string-ref c 0)))
|
||||
(r c)))
|
||||
|
||||
(define (generate-candidates word.path-hash)
|
||||
(for*/hash (((w p) word.path-hash)
|
||||
(w′ (candidates w)))
|
||||
(values w′ (cons w p))))
|
||||
|
||||
(define (hash-filter-keys keep-key? h)
|
||||
(for/hash (((k v) h) #:when (keep-key? k)) (values k v)))
|
||||
|
||||
(define (Word-ladder src dest (words (force *unixdict*)))
|
||||
(let loop ((edge (hash src null)) (unused (set-remove words src)))
|
||||
(let ((cands (generate-candidates edge)))
|
||||
(if (hash-has-key? cands dest)
|
||||
(reverse (cons dest (hash-ref cands dest)))
|
||||
(let ((new-edge (hash-filter-keys (curry set-member? unused) cands)))
|
||||
(if (hash-empty? new-edge)
|
||||
`(no-path-between ,src ,dest)
|
||||
(loop new-edge (set-subtract unused (list->set (hash-keys new-edge))))))))))
|
||||
|
||||
(module+ main
|
||||
(Word-ladder "boy" "man")
|
||||
(Word-ladder "girl" "lady")
|
||||
(Word-ladder "john" "jane")
|
||||
(Word-ladder "alien" "drool")
|
||||
(Word-ladder "child" "adult"))
|
||||
37
Task/Word-ladder/Raku/word-ladder.raku
Normal file
37
Task/Word-ladder/Raku/word-ladder.raku
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
constant %dict = 'unixdict.txt'.IO.lines
|
||||
.classify(*.chars)
|
||||
.map({ .key => .value.Set });
|
||||
|
||||
sub word_ladder ( Str $from, Str $to ) {
|
||||
die if $from.chars != $to.chars;
|
||||
|
||||
my $sized_dict = %dict{$from.chars};
|
||||
|
||||
my @workqueue = (($from,),);
|
||||
my $used = ($from => True).SetHash;
|
||||
while @workqueue {
|
||||
my @new_q;
|
||||
for @workqueue -> @words {
|
||||
my $last_word = @words.tail;
|
||||
my @new_tails = gather for 'a' .. 'z' -> $replacement_letter {
|
||||
for ^$last_word.chars -> $i {
|
||||
my $new_word = $last_word;
|
||||
$new_word.substr-rw($i, 1) = $replacement_letter;
|
||||
|
||||
next unless $new_word ∈ $sized_dict
|
||||
and not $new_word ∈ $used;
|
||||
take $new_word;
|
||||
$used{$new_word} = True;
|
||||
|
||||
return |@words, $new_word if $new_word eq $to;
|
||||
}
|
||||
}
|
||||
push @new_q, ( |@words, $_ ) for @new_tails;
|
||||
}
|
||||
@workqueue = @new_q;
|
||||
}
|
||||
}
|
||||
for <boy man>, <girl lady>, <john jane>, <child adult> -> ($from, $to) {
|
||||
say word_ladder($from, $to)
|
||||
// "$from into $to cannot be done";
|
||||
}
|
||||
43
Task/Word-ladder/Ruby/word-ladder.rb
Normal file
43
Task/Word-ladder/Ruby/word-ladder.rb
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
require "set"
|
||||
|
||||
Words = File.open("unixdict.txt").read.split("\n").
|
||||
group_by { |w| w.length }.map { |k, v| [k, Set.new(v)] }.
|
||||
to_h
|
||||
|
||||
def word_ladder(from, to)
|
||||
raise "Length mismatch" unless from.length == to.length
|
||||
sized_words = Words[from.length]
|
||||
work_queue = [[from]]
|
||||
used = Set.new [from]
|
||||
while work_queue.length > 0
|
||||
new_q = []
|
||||
work_queue.each do |words|
|
||||
last_word = words[-1]
|
||||
new_tails = Enumerator.new do |enum|
|
||||
("a".."z").each do |replacement_letter|
|
||||
last_word.length.times do |i|
|
||||
new_word = last_word.clone
|
||||
new_word[i] = replacement_letter
|
||||
next unless sized_words.include? new_word and
|
||||
not used.include? new_word
|
||||
enum.yield new_word
|
||||
used.add new_word
|
||||
return words + [new_word] if new_word == to
|
||||
end
|
||||
end
|
||||
end
|
||||
new_tails.each do |t|
|
||||
new_q.push(words + [t])
|
||||
end
|
||||
end
|
||||
work_queue = new_q
|
||||
end
|
||||
end
|
||||
|
||||
[%w<boy man>, %w<girl lady>, %w<john jane>, %w<child adult>].each do |from, to|
|
||||
if ladder = word_ladder(from, to)
|
||||
puts ladder.join " → "
|
||||
else
|
||||
puts "#{from} into #{to} cannot be done"
|
||||
end
|
||||
end
|
||||
61
Task/Word-ladder/Swift/word-ladder.swift
Normal file
61
Task/Word-ladder/Swift/word-ladder.swift
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import Foundation
|
||||
|
||||
func oneAway(string1: [Character], string2: [Character]) -> Bool {
|
||||
if string1.count != string2.count {
|
||||
return false
|
||||
}
|
||||
var result = false
|
||||
var i = 0
|
||||
while i < string1.count {
|
||||
if string1[i] != string2[i] {
|
||||
if result {
|
||||
return false
|
||||
}
|
||||
result = true
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func wordLadder(words: [[Character]], from: String, to: String) {
|
||||
let fromCh = Array(from)
|
||||
let toCh = Array(to)
|
||||
var poss = words.filter{$0.count == fromCh.count}
|
||||
var queue: [[[Character]]] = [[fromCh]]
|
||||
while !queue.isEmpty {
|
||||
var curr = queue[0]
|
||||
let last = curr[curr.count - 1]
|
||||
queue.removeFirst()
|
||||
let next = poss.filter{oneAway(string1: $0, string2: last)}
|
||||
if next.contains(toCh) {
|
||||
curr.append(toCh)
|
||||
print(curr.map{String($0)}.joined(separator: " -> "))
|
||||
return
|
||||
}
|
||||
poss.removeAll(where: {next.contains($0)})
|
||||
for str in next {
|
||||
var temp = curr
|
||||
temp.append(str)
|
||||
queue.append(temp)
|
||||
}
|
||||
}
|
||||
print("\(from) into \(to) cannot be done.")
|
||||
}
|
||||
|
||||
do {
|
||||
let words = try String(contentsOfFile: "unixdict.txt", encoding: String.Encoding.ascii)
|
||||
.components(separatedBy: "\n")
|
||||
.filter{!$0.isEmpty}
|
||||
.map{Array($0)}
|
||||
wordLadder(words: words, from: "man", to: "boy")
|
||||
wordLadder(words: words, from: "girl", to: "lady")
|
||||
wordLadder(words: words, from: "john", to: "jane")
|
||||
wordLadder(words: words, from: "child", to: "adult")
|
||||
wordLadder(words: words, from: "cat", to: "dog")
|
||||
wordLadder(words: words, from: "lead", to: "gold")
|
||||
wordLadder(words: words, from: "white", to: "black")
|
||||
wordLadder(words: words, from: "bubble", to: "tickle")
|
||||
} catch {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
41
Task/Word-ladder/Wren/word-ladder.wren
Normal file
41
Task/Word-ladder/Wren/word-ladder.wren
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import "io" for File
|
||||
import "/sort" for Find
|
||||
|
||||
var words = File.read("unixdict.txt").trim().split("\n")
|
||||
|
||||
var oneAway = Fn.new { |a, b|
|
||||
var sum = 0
|
||||
for (i in 0...a.count) if (a[i] != b[i]) sum = sum + 1
|
||||
return sum == 1
|
||||
}
|
||||
|
||||
var wordLadder = Fn.new { |a, b|
|
||||
var l = a.count
|
||||
var poss = words.where { |w| w.count == l }.toList
|
||||
var todo = [[a]]
|
||||
while (todo.count > 0) {
|
||||
var curr = todo[0]
|
||||
todo = todo[1..-1]
|
||||
var next = poss.where { |w| oneAway.call(w, curr[-1]) }.toList
|
||||
if (Find.first(next, b) != -1) {
|
||||
curr.add(b)
|
||||
System.print(curr.join(" -> "))
|
||||
return
|
||||
}
|
||||
poss = poss.where { |p| !next.contains(p) }.toList
|
||||
for (i in 0...next.count) {
|
||||
var temp = curr.toList
|
||||
temp.add(next[i])
|
||||
todo.add(temp)
|
||||
}
|
||||
}
|
||||
System.print("%(a) into %(b) cannot be done.")
|
||||
}
|
||||
|
||||
var pairs = [
|
||||
["boy", "man"],
|
||||
["girl", "lady"],
|
||||
["john", "jane"],
|
||||
["child", "adult"]
|
||||
]
|
||||
for (pair in pairs) wordLadder.call(pair[0], pair[1])
|
||||
Loading…
Add table
Add a link
Reference in a new issue