Changeable words: Difference between revisions
m →{{header|REXX}}: added a usable word count to the report title. |
Added Java solution |
||
Line 432: | Line 432: | ||
} |
} |
||
} |
} |
||
} |
|||
}</lang> |
|||
{{out}} |
|||
<pre> |
|||
Changeable words in unixdict.txt: |
|||
1: aristotelean -> aristotelian |
|||
2: aristotelian -> aristotelean |
|||
3: claustrophobia -> claustrophobic |
|||
4: claustrophobic -> claustrophobia |
|||
5: committeeman -> committeemen |
|||
6: committeemen -> committeeman |
|||
7: committeewoman -> committeewomen |
|||
8: committeewomen -> committeewoman |
|||
9: complementary -> complimentary |
|||
10: complimentary -> complementary |
|||
11: confirmation -> conformation |
|||
12: conformation -> confirmation |
|||
13: congresswoman -> congresswomen |
|||
14: congresswomen -> congresswoman |
|||
15: councilwoman -> councilwomen |
|||
16: councilwomen -> councilwoman |
|||
17: craftsperson -> draftsperson |
|||
18: draftsperson -> craftsperson |
|||
19: eavesdropped -> eavesdropper |
|||
20: eavesdropper -> eavesdropped |
|||
21: frontiersman -> frontiersmen |
|||
22: frontiersmen -> frontiersman |
|||
23: handicraftsman -> handicraftsmen |
|||
24: handicraftsmen -> handicraftsman |
|||
25: incommutable -> incomputable |
|||
26: incomputable -> incommutable |
|||
27: installation -> instillation |
|||
28: instillation -> installation |
|||
29: kaleidescope -> kaleidoscope |
|||
30: kaleidoscope -> kaleidescope |
|||
31: neuroanatomy -> neuroanotomy |
|||
32: neuroanotomy -> neuroanatomy |
|||
33: newspaperman -> newspapermen |
|||
34: newspapermen -> newspaperman |
|||
35: nonagenarian -> nonogenarian |
|||
36: nonogenarian -> nonagenarian |
|||
37: onomatopoeia -> onomatopoeic |
|||
38: onomatopoeic -> onomatopoeia |
|||
39: philanthrope -> philanthropy |
|||
40: philanthropy -> philanthrope |
|||
41: prescription -> proscription |
|||
42: proscription -> prescription |
|||
43: schizophrenia -> schizophrenic |
|||
44: schizophrenic -> schizophrenia |
|||
45: shakespearean -> shakespearian |
|||
46: shakespearian -> shakespearean |
|||
47: spectroscope -> spectroscopy |
|||
48: spectroscopy -> spectroscope |
|||
49: underclassman -> underclassmen |
|||
50: underclassmen -> underclassman |
|||
51: upperclassman -> upperclassmen |
|||
52: upperclassmen -> upperclassman |
|||
</pre> |
|||
=={{header|Java}}== |
|||
{{trans|Go}} |
|||
<lang java>import java.io.*; |
|||
import java.util.*; |
|||
public class ChangeableWords { |
|||
public static void main(String[] args) { |
|||
try { |
|||
final String fileName = "unixdict.txt"; |
|||
List<String> dictionary = new ArrayList<>(); |
|||
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) { |
|||
String line; |
|||
while ((line = reader.readLine()) != null) { |
|||
if (line.length() > 11) |
|||
dictionary.add(line); |
|||
} |
|||
} |
|||
System.out.println(String.format("Changeable words in %s:", fileName)); |
|||
int n = 1; |
|||
for (String word1 : dictionary) { |
|||
for (String word2 : dictionary) { |
|||
if (word1 != word2 && hammingDistance(word1, word2) == 1) |
|||
System.out.println(String.format("%2d: %-14s -> %s", n++, word1, word2)); |
|||
} |
|||
} |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
private static int hammingDistance(String str1, String str2) { |
|||
int len1 = str1.length(); |
|||
int len2 = str2.length(); |
|||
if (len1 != len2) |
|||
return 0; |
|||
int count = 0; |
|||
for (int i = 0; i < len1; ++i) { |
|||
if (str1.charAt(i) != str2.charAt(i)) |
|||
++count; |
|||
// don't care about counts > 2 in this case |
|||
if (count == 2) |
|||
break; |
|||
} |
|||
return count; |
|||
} |
} |
||
}</lang> |
}</lang> |
Revision as of 16:34, 15 December 2020
- Task
Using the dictionary unixdict.txt, change one letter in a word, and if the changed word occurs in the dictionary,
then display the original word and the changed word here (on this page).
The length of any word shown should have a length > 11.
- Metrics
- Counting
- Word frequency
- Letter frequency
- Jewels and stones
- I before E except after C
- Bioinformatics/base count
- Count occurrences of a substring
- Count how many vowels and consonants occur in a string
- Remove/replace
- XXXX redacted
- Conjugate a Latin verb
- Remove vowels from a string
- String interpolation (included)
- Strip block comments
- Strip comments from a string
- Strip a set of characters from a string
- Strip whitespace from a string -- top and tail
- Strip control codes and extended characters from a string
- Anagrams/Derangements/shuffling
- Word wheel
- ABC problem
- Sattolo cycle
- Knuth shuffle
- Ordered words
- Superpermutation minimisation
- Textonyms (using a phone text pad)
- Anagrams
- Anagrams/Deranged anagrams
- Permutations/Derangements
- Find/Search/Determine
- ABC words
- Odd words
- Word ladder
- Semordnilap
- Word search
- Wordiff (game)
- String matching
- Tea cup rim text
- Alternade words
- Changeable words
- State name puzzle
- String comparison
- Unique characters
- Unique characters in each string
- Extract file extension
- Levenshtein distance
- Palindrome detection
- Common list elements
- Longest common suffix
- Longest common prefix
- Compare a list of strings
- Longest common substring
- Find common directory path
- Words from neighbour ones
- Change e letters to i in words
- Non-continuous subsequences
- Longest common subsequence
- Longest palindromic substrings
- Longest increasing subsequence
- Words containing "the" substring
- Sum of the digits of n is substring of n
- Determine if a string is numeric
- Determine if a string is collapsible
- Determine if a string is squeezable
- Determine if a string has all unique characters
- Determine if a string has all the same characters
- Longest substrings without repeating characters
- Find words which contains all the vowels
- Find words which contain the most consonants
- Find words which contains more than 3 vowels
- Find words whose first and last three letters are equal
- Find words with alternating vowels and consonants
- Formatting
- Substring
- Rep-string
- Word wrap
- String case
- Align columns
- Literals/String
- Repeat a string
- Brace expansion
- Brace expansion using ranges
- Reverse a string
- Phrase reversals
- Comma quibbling
- Special characters
- String concatenation
- Substring/Top and tail
- Commatizing numbers
- Reverse words in a string
- Suffixation of decimal numbers
- Long literals, with continuations
- Numerical and alphabetical suffixes
- Abbreviations, easy
- Abbreviations, simple
- Abbreviations, automatic
- Song lyrics/poems/Mad Libs/phrases
- Mad Libs
- Magic 8-ball
- 99 bottles of beer
- The Name Game (a song)
- The Old lady swallowed a fly
- The Twelve Days of Christmas
- Tokenize
- Text between
- Tokenize a string
- Word break problem
- Tokenize a string with escaping
- Split a character string based on change of character
- Sequences
C
<lang c>#include <stdio.h>
- include <stdlib.h>
- include <string.h>
- define MAX_WORD_SIZE 32
typedef struct string_tag {
size_t length; char str[MAX_WORD_SIZE];
} string_t;
void fatal(const char* message) {
fprintf(stderr, "%s\n", message); exit(1);
}
void* xmalloc(size_t n) {
void* ptr = malloc(n); if (ptr == NULL) fatal("Out of memory"); return ptr;
}
void* xrealloc(void* p, size_t n) {
void* ptr = realloc(p, n); if (ptr == NULL) fatal("Out of memory"); return ptr;
}
int hamming_distance(const string_t* str1, const string_t* str2) {
size_t len1 = str1->length; size_t len2 = str2->length; if (len1 != len2) return 0; int count = 0; const char* s1 = str1->str; const char* s2 = str2->str; for (size_t i = 0; i < len1; ++i) { if (s1[i] != s2[i]) ++count; // don't care about counts > 2 in this case if (count == 2) break; } return count;
}
int main(int argc, char** argv) {
const char* filename = argc < 2 ? "unixdict.txt" : argv[1]; FILE* in = fopen(filename, "r"); if (!in) { perror(filename); return EXIT_FAILURE; } char line[MAX_WORD_SIZE]; size_t size = 0, capacity = 1024; string_t* dictionary = xmalloc(sizeof(string_t) * capacity); while (fgets(line, sizeof(line), in)) { if (size == capacity) { capacity *= 2; dictionary = xrealloc(dictionary, sizeof(string_t) * capacity); } size_t len = strlen(line) - 1; if (len > 11) { string_t* str = &dictionary[size]; str->length = len; memcpy(str->str, line, len); str->str[len] = '\0'; ++size; } } fclose(in); printf("Changeable words in %s:\n", filename); int n = 1; for (size_t i = 0; i < size; ++i) { const string_t* str1 = &dictionary[i]; for (size_t j = 0; j < size; ++j) { const string_t* str2 = &dictionary[j]; if (i != j && hamming_distance(str1, str2) == 1) printf("%2d: %-14s -> %s\n", n++, str1->str, str2->str); } } free(dictionary); return EXIT_SUCCESS;
}</lang>
- Output:
Changeable words in unixdict.txt: 1: aristotelean -> aristotelian 2: aristotelian -> aristotelean 3: claustrophobia -> claustrophobic 4: claustrophobic -> claustrophobia 5: committeeman -> committeemen 6: committeemen -> committeeman 7: committeewoman -> committeewomen 8: committeewomen -> committeewoman 9: complementary -> complimentary 10: complimentary -> complementary 11: confirmation -> conformation 12: conformation -> confirmation 13: congresswoman -> congresswomen 14: congresswomen -> congresswoman 15: councilwoman -> councilwomen 16: councilwomen -> councilwoman 17: craftsperson -> draftsperson 18: draftsperson -> craftsperson 19: eavesdropped -> eavesdropper 20: eavesdropper -> eavesdropped 21: frontiersman -> frontiersmen 22: frontiersmen -> frontiersman 23: handicraftsman -> handicraftsmen 24: handicraftsmen -> handicraftsman 25: incommutable -> incomputable 26: incomputable -> incommutable 27: installation -> instillation 28: instillation -> installation 29: kaleidescope -> kaleidoscope 30: kaleidoscope -> kaleidescope 31: neuroanatomy -> neuroanotomy 32: neuroanotomy -> neuroanatomy 33: newspaperman -> newspapermen 34: newspapermen -> newspaperman 35: nonagenarian -> nonogenarian 36: nonogenarian -> nonagenarian 37: onomatopoeia -> onomatopoeic 38: onomatopoeic -> onomatopoeia 39: philanthrope -> philanthropy 40: philanthropy -> philanthrope 41: prescription -> proscription 42: proscription -> prescription 43: schizophrenia -> schizophrenic 44: schizophrenic -> schizophrenia 45: shakespearean -> shakespearian 46: shakespearian -> shakespearean 47: spectroscope -> spectroscopy 48: spectroscopy -> spectroscope 49: underclassman -> underclassmen 50: underclassmen -> underclassman 51: upperclassman -> upperclassmen 52: upperclassmen -> upperclassman
C++
<lang cpp>#include <cstdlib>
- include <fstream>
- include <iomanip>
- include <iostream>
- include <string>
- include <vector>
int hamming_distance(const std::string& str1, const std::string& str2) {
size_t len1 = str1.size(); size_t len2 = str2.size(); if (len1 != len2) return 0; int count = 0; for (size_t i = 0; i < len1; ++i) { if (str1[i] != str2[i]) ++count; // don't care about counts > 2 in this case if (count == 2) break; } return count;
}
int main(int argc, char** argv) {
const char* filename(argc < 2 ? "unixdict.txt" : argv[1]); std::ifstream in(filename); if (!in) { std::cerr << "Cannot open file '" << filename << "'.\n"; return EXIT_FAILURE; } std::string line; std::vector<std::string> dictionary; while (getline(in, line)) { if (line.size() > 11) dictionary.push_back(line); } std::cout << "Changeable words in " << filename << ":\n"; int n = 1; for (const std::string& word1 : dictionary) { for (const std::string& word2 : dictionary) { if (hamming_distance(word1, word2) == 1) std::cout << std::setw(2) << std::right << n++ << ": " << std::setw(14) << std::left << word1 << " -> " << word2 << '\n'; } } return EXIT_SUCCESS;
}</lang>
- Output:
Changeable words in unixdict.txt: 1: aristotelean -> aristotelian 2: aristotelian -> aristotelean 3: claustrophobia -> claustrophobic 4: claustrophobic -> claustrophobia 5: committeeman -> committeemen 6: committeemen -> committeeman 7: committeewoman -> committeewomen 8: committeewomen -> committeewoman 9: complementary -> complimentary 10: complimentary -> complementary 11: confirmation -> conformation 12: conformation -> confirmation 13: congresswoman -> congresswomen 14: congresswomen -> congresswoman 15: councilwoman -> councilwomen 16: councilwomen -> councilwoman 17: craftsperson -> draftsperson 18: draftsperson -> craftsperson 19: eavesdropped -> eavesdropper 20: eavesdropper -> eavesdropped 21: frontiersman -> frontiersmen 22: frontiersmen -> frontiersman 23: handicraftsman -> handicraftsmen 24: handicraftsmen -> handicraftsman 25: incommutable -> incomputable 26: incomputable -> incommutable 27: installation -> instillation 28: instillation -> installation 29: kaleidescope -> kaleidoscope 30: kaleidoscope -> kaleidescope 31: neuroanatomy -> neuroanotomy 32: neuroanotomy -> neuroanatomy 33: newspaperman -> newspapermen 34: newspapermen -> newspaperman 35: nonagenarian -> nonogenarian 36: nonogenarian -> nonagenarian 37: onomatopoeia -> onomatopoeic 38: onomatopoeic -> onomatopoeia 39: philanthrope -> philanthropy 40: philanthropy -> philanthrope 41: prescription -> proscription 42: proscription -> prescription 43: schizophrenia -> schizophrenic 44: schizophrenic -> schizophrenia 45: shakespearean -> shakespearian 46: shakespearian -> shakespearean 47: spectroscope -> spectroscopy 48: spectroscopy -> spectroscope 49: underclassman -> underclassmen 50: underclassmen -> underclassman 51: upperclassman -> upperclassmen 52: upperclassmen -> upperclassman
FreeBASIC
Brute force method, reuses some code from Odd_words#FreeBASIC. <lang freebasic>
- define NULL 0
type node
word as string*32 'enough space to store any word in the dictionary nxt as node ptr
end type
function addword( tail as node ptr, word as string ) as node ptr
'allocates memory for a new node, links the previous tail to it, 'and returns the address of the new node dim as node ptr newnode = allocate(sizeof(node)) tail->nxt = newnode newnode->nxt = NULL newnode->word = word return newnode
end function
function length( word as string ) as uinteger
'necessary replacement for the built-in len function, which in this 'case would always return 32 for i as uinteger = 1 to 32 if asc(mid(word,i,1)) = 0 then return i-1 next i return 999
end function
dim as string word dim as node ptr tail = allocate( sizeof(node) ) dim as node ptr head = tail, curr = head, currj tail->nxt = NULL tail->word = "XXXXHEADER"
open "unixdict.txt" for input as #1 while true
line input #1, word if word = "" then exit while if length(word)>11 then tail = addword( tail, word )
wend close #1
dim as string tempword
while curr->nxt <> NULL
for i as uinteger = 1 to length(curr->word) for j as uinteger = 97 to 122 if j = asc(mid(curr->word,i,1)) then continue for tempword = left(curr->word,i-1)+ chr(j) + mid(curr->word, i+1, length(curr->word)-i) currj = head while currj->nxt <> NULL if tempword = currj->word then print left(curr->word,length(curr->word));" ---> ";tempword currj = currj->nxt wend next j next i curr = curr->nxt
wend</lang>
- Output:
aristotelean ---> aristotelian aristotelian ---> aristotelean claustrophobia ---> claustrophobic claustrophobic ---> claustrophobia committeeman ---> committeemen committeemen ---> committeeman committeewoman ---> committeewomen committeewomen ---> committeewoman complementary ---> complimentary complimentary ---> complementary confirmation ---> conformation conformation ---> confirmation congresswoman ---> congresswomen congresswomen ---> congresswoman councilwoman ---> councilwomen councilwomen ---> councilwoman craftsperson ---> draftsperson draftsperson ---> craftsperson eavesdropped ---> eavesdropper eavesdropper ---> eavesdropped frontiersman ---> frontiersmen frontiersmen ---> frontiersman handicraftsman ---> handicraftsmen handicraftsmen ---> handicraftsman incommutable ---> incomputable incomputable ---> incommutable installation ---> instillation instillation ---> installation kaleidescope ---> kaleidoscope kaleidoscope ---> kaleidescope neuroanatomy ---> neuroanotomy neuroanotomy ---> neuroanatomy newspaperman ---> newspapermen newspapermen ---> newspaperman nonagenarian ---> nonogenarian nonogenarian ---> nonagenarian onomatopoeia ---> onomatopoeic onomatopoeic ---> onomatopoeia philanthrope ---> philanthropy philanthropy ---> philanthrope prescription ---> proscription proscription ---> prescription schizophrenia ---> schizophrenic schizophrenic ---> schizophrenia shakespearean ---> shakespearian shakespearian ---> shakespearean spectroscope ---> spectroscopy spectroscopy ---> spectroscope underclassman ---> underclassmen underclassmen ---> underclassman upperclassman ---> upperclassmen upperclassmen ---> upperclassman
Go
<lang go>package main
import (
"bytes" "fmt" "io/ioutil" "log" "unicode/utf8"
)
func hammingDist(s1, s2 string) int {
r1 := []rune(s1) // in case there are non-ASCII characters r2 := []rune(s2) // ditto if len(r1) != len(r2) { return 0 } count := 0 for i := 0; i < len(r1); i++ { if r1[i] != r2[i] { count++ if count == 2 { break // don't care about counts > 2 } } } return count
}
func main() {
wordList := "unixdict.txt" b, err := ioutil.ReadFile(wordList) if err != nil { log.Fatal("Error reading file") } bwords := bytes.Fields(b) var words []string for _, bword := range bwords { s := string(bword) if utf8.RuneCountInString(s) > 11 { words = append(words, s) } } count := 0 fmt.Println("Changeable words in", wordList, "\b:") for _, word1 := range words { for _, word2 := range words { if word1 != word2 && hammingDist(word1, word2) == 1 { count++ fmt.Printf("%2d: %-14s -> %s\n", count, word1, word2) } } }
}</lang>
- Output:
Changeable words in unixdict.txt: 1: aristotelean -> aristotelian 2: aristotelian -> aristotelean 3: claustrophobia -> claustrophobic 4: claustrophobic -> claustrophobia 5: committeeman -> committeemen 6: committeemen -> committeeman 7: committeewoman -> committeewomen 8: committeewomen -> committeewoman 9: complementary -> complimentary 10: complimentary -> complementary 11: confirmation -> conformation 12: conformation -> confirmation 13: congresswoman -> congresswomen 14: congresswomen -> congresswoman 15: councilwoman -> councilwomen 16: councilwomen -> councilwoman 17: craftsperson -> draftsperson 18: draftsperson -> craftsperson 19: eavesdropped -> eavesdropper 20: eavesdropper -> eavesdropped 21: frontiersman -> frontiersmen 22: frontiersmen -> frontiersman 23: handicraftsman -> handicraftsmen 24: handicraftsmen -> handicraftsman 25: incommutable -> incomputable 26: incomputable -> incommutable 27: installation -> instillation 28: instillation -> installation 29: kaleidescope -> kaleidoscope 30: kaleidoscope -> kaleidescope 31: neuroanatomy -> neuroanotomy 32: neuroanotomy -> neuroanatomy 33: newspaperman -> newspapermen 34: newspapermen -> newspaperman 35: nonagenarian -> nonogenarian 36: nonogenarian -> nonagenarian 37: onomatopoeia -> onomatopoeic 38: onomatopoeic -> onomatopoeia 39: philanthrope -> philanthropy 40: philanthropy -> philanthrope 41: prescription -> proscription 42: proscription -> prescription 43: schizophrenia -> schizophrenic 44: schizophrenic -> schizophrenia 45: shakespearean -> shakespearian 46: shakespearian -> shakespearean 47: spectroscope -> spectroscopy 48: spectroscopy -> spectroscope 49: underclassman -> underclassmen 50: underclassmen -> underclassman 51: upperclassman -> upperclassmen 52: upperclassmen -> upperclassman
Java
<lang java>import java.io.*; import java.util.*;
public class ChangeableWords {
public static void main(String[] args) { try { final String fileName = "unixdict.txt"; List<String> dictionary = new ArrayList<>(); try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) { String line; while ((line = reader.readLine()) != null) { if (line.length() > 11) dictionary.add(line); } } System.out.println(String.format("Changeable words in %s:", fileName)); int n = 1; for (String word1 : dictionary) { for (String word2 : dictionary) { if (word1 != word2 && hammingDistance(word1, word2) == 1) System.out.println(String.format("%2d: %-14s -> %s", n++, word1, word2)); } } } catch (Exception e) { e.printStackTrace(); } }
private static int hammingDistance(String str1, String str2) { int len1 = str1.length(); int len2 = str2.length(); if (len1 != len2) return 0; int count = 0; for (int i = 0; i < len1; ++i) { if (str1.charAt(i) != str2.charAt(i)) ++count; // don't care about counts > 2 in this case if (count == 2) break; } return count; }
}</lang>
- Output:
Changeable words in unixdict.txt: 1: aristotelean -> aristotelian 2: aristotelian -> aristotelean 3: claustrophobia -> claustrophobic 4: claustrophobic -> claustrophobia 5: committeeman -> committeemen 6: committeemen -> committeeman 7: committeewoman -> committeewomen 8: committeewomen -> committeewoman 9: complementary -> complimentary 10: complimentary -> complementary 11: confirmation -> conformation 12: conformation -> confirmation 13: congresswoman -> congresswomen 14: congresswomen -> congresswoman 15: councilwoman -> councilwomen 16: councilwomen -> councilwoman 17: craftsperson -> draftsperson 18: draftsperson -> craftsperson 19: eavesdropped -> eavesdropper 20: eavesdropper -> eavesdropped 21: frontiersman -> frontiersmen 22: frontiersmen -> frontiersman 23: handicraftsman -> handicraftsmen 24: handicraftsmen -> handicraftsman 25: incommutable -> incomputable 26: incomputable -> incommutable 27: installation -> instillation 28: instillation -> installation 29: kaleidescope -> kaleidoscope 30: kaleidoscope -> kaleidescope 31: neuroanatomy -> neuroanotomy 32: neuroanotomy -> neuroanatomy 33: newspaperman -> newspapermen 34: newspapermen -> newspaperman 35: nonagenarian -> nonogenarian 36: nonogenarian -> nonagenarian 37: onomatopoeia -> onomatopoeic 38: onomatopoeic -> onomatopoeia 39: philanthrope -> philanthropy 40: philanthropy -> philanthrope 41: prescription -> proscription 42: proscription -> prescription 43: schizophrenia -> schizophrenic 44: schizophrenic -> schizophrenia 45: shakespearean -> shakespearian 46: shakespearian -> shakespearean 47: spectroscope -> spectroscopy 48: spectroscopy -> spectroscope 49: underclassman -> underclassmen 50: underclassmen -> underclassman 51: upperclassman -> upperclassmen 52: upperclassmen -> upperclassman
Julia
After finding for example "are <=> art", we do not also list the redundant "art <=> are" below. <lang julia>function changeable(wordfile, lengthover)
words, results = split(read(wordfile, String)), [] worddict = Dict(w => length(w) for w in words) for w in words len = get(worddict, w, 0) if len <= lengthover continue end alternatives = [w[1:p[1]-1] * p[2] * w[p[1]+1:end] for p in Iterators.product(1:len, 'a':'z')] for a in alternatives if a != w && haskey(worddict, a) push!(results, join(sort([w, a]), " <=> ")) break end end end println(join(sort(unique(results)), "\n"))
end
changeable("unixdict.txt", 11)
</lang>
- Output:
aristotelean <=> aristotelian claustrophobia <=> claustrophobic committeeman <=> committeemen committeewoman <=> committeewomen complementary <=> complimentary confirmation <=> conformation congresswoman <=> congresswomen councilwoman <=> councilwomen craftsperson <=> draftsperson eavesdropped <=> eavesdropper frontiersman <=> frontiersmen handicraftsman <=> handicraftsmen incommutable <=> incomputable installation <=> instillation kaleidescope <=> kaleidoscope neuroanatomy <=> neuroanotomy newspaperman <=> newspapermen nonagenarian <=> nonogenarian onomatopoeia <=> onomatopoeic philanthrope <=> philanthropy prescription <=> proscription schizophrenia <=> schizophrenic shakespearean <=> shakespearian spectroscope <=> spectroscopy underclassman <=> underclassmen upperclassman <=> upperclassmen
Perl
<lang perl>#!/usr/bin/perl
use strict; use warnings;
my @words; @ARGV = 'unixdict.txt'; while( <> )
{ chomp; length > 11 or next; for my $prev ( @{ $words[length] } ) { ($prev ^ $_) =~ tr/\0//c == 1 and printf "%30s <-> %s\n", $prev, $_; } push @{ $words[length] }, $_; }</lang>
- Output:
aristotelean <-> aristotelian claustrophobia <-> claustrophobic committeeman <-> committeemen committeewoman <-> committeewomen complementary <-> complimentary confirmation <-> conformation congresswoman <-> congresswomen councilwoman <-> councilwomen craftsperson <-> draftsperson eavesdropped <-> eavesdropper frontiersman <-> frontiersmen handicraftsman <-> handicraftsmen incommutable <-> incomputable installation <-> instillation kaleidescope <-> kaleidoscope neuroanatomy <-> neuroanotomy newspaperman <-> newspapermen nonagenarian <-> nonogenarian onomatopoeia <-> onomatopoeic philanthrope <-> philanthropy prescription <-> proscription schizophrenia <-> schizophrenic shakespearean <-> shakespearian spectroscope <-> spectroscopy underclassman <-> underclassmen upperclassman <-> upperclassmen
Phix
<lang Phix>function changeable(string a, b)
return length(a)=length(b) and sum(sq_ne(a,b))=1
end function
function over11(string word) return length(word)>11 end function sequence words = filter(get_text("demo/unixdict.txt",GT_LF_STRIPPED),over11),
res = {}
for i=1 to length(words) do
for j=i+1 to length(words) do if changeable(words[i],words[j]) then res = append(res,words[i]&" <=> "&words[j]) end if end for
end for printf(1,"%d changeable words found:\n%s\n",{length(res),join(shorten(res,"",3),"\n")})</lang>
- Output:
26 changeable words found: aristotelean <=> aristotelian claustrophobia <=> claustrophobic committeeman <=> committeemen ... spectroscope <=> spectroscopy underclassman <=> underclassmen upperclassman <=> upperclassmen
Python
<lang python>from collections import defaultdict, Counter
def getwords(minlength=11, fname='unixdict.txt'):
"Return set of lowercased words of > given number of characters" with open(fname) as f: words = f.read().strip().lower().split() return {w for w in words if len(w) > minlength}
words11 = getwords() word_minus_1 = defaultdict(list) # map word minus char to (word, index) pairs minus_1_to_word = defaultdict(list) # map word minus char to word
for w in words11:
for i in range(len(w)): minus_1 = w[:i] + w[i+1:] word_minus_1[minus_1].append((w, i)) # minus one char if minus_1 in words11: minus_1_to_word[minus_1].append(w)
cwords = set() # Changed char words for _, v in word_minus_1.items():
if len(v) >1: change_indices = Counter(i for wrd, i in v) change_words = set(wrd for wrd, i in v) words_changed = None if len(change_words) > 1 and change_indices.most_common(1)[0][1] > 1: words_changed = [wrd for wrd, i in v if change_indices[i] > 1] if words_changed: cwords.add(tuple(sorted(words_changed)))
print(f"{len(minus_1_to_word)} words that are from deleting a char from other words:") for k, v in sorted(minus_1_to_word.items()):
print(f" {k:12} From {', '.join(v)}")
print(f"\n{len(cwords)} words that are from changing a char from other words:") for v in sorted(cwords):
print(f" {v[0]:12} From {', '.join(v[1:])}")</lang>
- Output:
6 words that are from deleting a char from other words: chromatograph From chromatography electroencephalograph From electroencephalography evolutionary From revolutionary michelangelo From michaelangelo spectrograph From spectrography tetrafluoride From tetrafluouride 26 words that are from changing a char from other words: aristotelean From aristotelian claustrophobia From claustrophobic committeeman From committeemen committeewoman From committeewomen complementary From complimentary confirmation From conformation congresswoman From congresswomen councilwoman From councilwomen craftsperson From draftsperson eavesdropped From eavesdropper frontiersman From frontiersmen handicraftsman From handicraftsmen incommutable From incomputable installation From instillation kaleidescope From kaleidoscope neuroanatomy From neuroanotomy newspaperman From newspapermen nonagenarian From nonogenarian onomatopoeia From onomatopoeic philanthrope From philanthropy prescription From proscription schizophrenia From schizophrenic shakespearean From shakespearian spectroscope From spectroscopy underclassman From underclassmen upperclassman From upperclassmen
Raku
Sorensen-Dice is very fast to calculate similarities but isn't great for detecting small changes. Levenshtein is great for detecting small changes but isn't very fast.
Get the best of both worlds by doing an initial filter with Sorensen, then get exact results with Levenshtein. <lang perl6>use Text::Levenshtein; use Text::Sorensen :sorensen;
my @words = grep {.chars > 11}, 'unixdict.txt'.IO.words;
my %bi-grams = @words.map: { $_ => .&bi-gram };
my %skip = @words.map: { $_ => 0 };
say (++$).fmt('%2d'), |$_ for @words.hyper.map: -> $this {
next if %skip{$this}; my ($word, @sorensens) = sorensen($this, %bi-grams); next unless @sorensens.=grep: { 1 > .[0] > .8 }; @sorensens = @sorensens»[1].grep: {$this.chars == .chars}; my @levenshtein = distance($this, @sorensens).grep: * == 1, :k; next unless +@levenshtein; %skip{$_}++ for @sorensens[@levenshtein]; ": {$this.fmt('%14s')} <-> ", @sorensens[@levenshtein].join: ', ';
}</lang>
- Output:
1: aristotelean <-> aristotelian 2: claustrophobia <-> claustrophobic 3: committeeman <-> committeemen 4: committeewoman <-> committeewomen 5: complimentary <-> complementary 6: confirmation <-> conformation 7: congresswoman <-> congresswomen 8: councilwoman <-> councilwomen 9: draftsperson <-> craftsperson 10: eavesdropped <-> eavesdropper 11: frontiersman <-> frontiersmen 12: handicraftsman <-> handicraftsmen 13: incommutable <-> incomputable 14: installation <-> instillation 15: kaleidescope <-> kaleidoscope 16: neuroanatomy <-> neuroanotomy 17: newspaperman <-> newspapermen 18: nonagenarian <-> nonogenarian 19: onomatopoeia <-> onomatopoeic 20: philanthrope <-> philanthropy 21: proscription <-> prescription 22: schizophrenia <-> schizophrenic 23: shakespearean <-> shakespearian 24: spectroscope <-> spectroscopy 25: underclassman <-> underclassmen 26: upperclassman <-> upperclassmen
REXX
This REXX version doesn't care what order the words in the dictionary are in, nor does it care what
case (lower/upper/mixed) the words are in, the search for alternades is caseless.
It also allows the minimum length to be specified on the command line (CL) as well as the dictionary file identifier. <lang rexx>/*REXX program finds changeable words (within an identified dict.), changing any letter.*/ parse arg minL iFID . /*obtain optional arguments from the CL*/ if minL== | minL=="," then minL= 12 /*Not specified? Then use the default.*/ if iFID== | iFID=="," then iFID='unixdict.txt' /* " " " " " " */ @.= /*default value of any dictionary word.*/ wc= 0 /*WC: the word count of usable words.*/
do #=1 while lines(iFID)\==0 /*read each word in the file (word=X).*/ x= strip( linein( iFID) ) /*pick off a word from the input line. */ if length(x)<minL then iterate /*Is the word too short? Then skip it.*/ if \datatype(x, 'M') then iterate /* " " " not alphabetic? Skip it.*/ wc= wc+1; $.wc= x; upper x; @.x= $.wc /*bump word counter; save original case*/ end /*#*/ /* [↑] semaphore name is uppercased. */
say copies('─', 30) # "words ("wc 'usable words) in the dictionary file: ' iFID finds= 0 /*count of the changeable words found.*/ abc= 'abcdefghijklmnopqrstuvwxyz'; upper abc /*alphabet ordered by frequency of use.*/ Labc= length(abc) /*the length of the alphabet to be used*/
do j=1 for wc; L= length($.j) /*process all the words that were found*/ if \datatype($.j, 'M') then iterate /*verify that the word is all letters. */ x= $.j; upper x /*get an uppercased version of the word*/ pad= left(, 9) /*PAD: used for indenting the output.*/ do k=1 for L; y= substr(x, k, 1) /*Y: the current letter being changed.*/ do c=1 for Labc /* [↓] change the Y letter to another.*/ ?= substr(abc, c, 1) /*get a new char to replace one in word*/ if ?==y then iterate /*Is this the same char? Then use next*/ new= overlay(?, x, k); upper new /*create a spanking new (changed) word.*/ if @.new== then iterate /*if the new word isn't a word, skip it*/ finds= finds + 1 /*bump count of changeable words found.*/ say pad left($.j, 30) @.new /*indent original word for readability.*/ end /*c*/ end /*k*/ end /*j*/
say copies('─', 30) finds ' changeable words found with a minimum length of ' minL</lang>
- output when using the default inputs:
────────────────────────────── 25105 words (1064 usable words) in the dictionary file: unixdict.txt aristotelean aristotelian aristotelian aristotelean claustrophobia claustrophobic claustrophobic claustrophobia committeeman committeemen committeemen committeeman committeewoman committeewomen committeewomen committeewoman complementary complimentary complimentary complementary confirmation conformation conformation confirmation congresswoman congresswomen congresswomen congresswoman councilwoman councilwomen councilwomen councilwoman craftsperson draftsperson draftsperson craftsperson eavesdropped eavesdropper eavesdropper eavesdropped frontiersman frontiersmen frontiersmen frontiersman handicraftsman handicraftsmen handicraftsmen handicraftsman incommutable incomputable incomputable incommutable installation instillation instillation installation kaleidescope kaleidoscope kaleidoscope kaleidescope neuroanatomy neuroanotomy neuroanotomy neuroanatomy newspaperman newspapermen newspapermen newspaperman nonagenarian nonogenarian nonogenarian nonagenarian onomatopoeia onomatopoeic onomatopoeic onomatopoeia philanthrope philanthropy philanthropy philanthrope prescription proscription proscription prescription schizophrenia schizophrenic schizophrenic schizophrenia shakespearean shakespearian shakespearian shakespearean spectroscope spectroscopy spectroscopy spectroscope underclassman underclassmen underclassmen underclassman upperclassman upperclassmen upperclassmen upperclassman ────────────────────────────── 52 changeable words found with a minimum length of 12
Ring
<lang ring> cStr = read("unixdict.txt") wordList = str2list(cStr) num = 0
see "working..." + nl
ln = len(wordList) for n = ln to 1 step -1
if len(wordList[n]) < 12 del(wordList,n) ok
next
see "Changable words are:" + nl
for n = 1 to len(wordList)
len = len(wordList[n]) for m = 1 to len abcList = "abcdefghijklmnopqrstuvwxyz" for abc in abcList str1 = left(wordList[n],m-1) str2 = abc str3 = right(wordList[n],len-m) tempWord = str1 + str2 + str3 ind = find(wordList,tempWord) bool = (ind > 0) and (wordList[n][m] != abc) if bool = 1 num = num + 1 see "" + num + ". " + wordList[n] + " >> " + tempWord + nl ok next next
next
see "done..." + nl </lang> Output:
working... Changable words are: 1. aristotelean >> aristotelian 2. aristotelian >> aristotelean 3. claustrophobia >> claustrophobic 4. claustrophobic >> claustrophobia 5. committeeman >> committeemen 6. committeemen >> committeeman 7. committeewoman >> committeewomen 8. committeewomen >> committeewoman 9. complementary >> complimentary 10. complimentary >> complementary 11. confirmation >> conformation 12. conformation >> confirmation 13. congresswoman >> congresswomen 14. congresswomen >> congresswoman 15. councilwoman >> councilwomen 16. councilwomen >> councilwoman 17. craftsperson >> draftsperson 18. draftsperson >> craftsperson 19. eavesdropped >> eavesdropper 20. eavesdropper >> eavesdropped 21. frontiersman >> frontiersmen 22. frontiersmen >> frontiersman 23. handicraftsman >> handicraftsmen 24. handicraftsmen >> handicraftsman 25. incommutable >> incomputable 26. incomputable >> incommutable 27. installation >> instillation 28. instillation >> installation 29. kaleidescope >> kaleidoscope 30. kaleidoscope >> kaleidescope 31. neuroanatomy >> neuroanotomy 32. neuroanotomy >> neuroanatomy 33. newspaperman >> newspapermen 34. newspapermen >> newspaperman 35. nonagenarian >> nonogenarian 36. nonogenarian >> nonagenarian 37. onomatopoeia >> onomatopoeic 38. onomatopoeic >> onomatopoeia 39. philanthrope >> philanthropy 40. philanthropy >> philanthrope 41. prescription >> proscription 42. proscription >> prescription 43. schizophrenia >> schizophrenic 44. schizophrenic >> schizophrenia 45. shakespearean >> shakespearian 46. shakespearian >> shakespearean 47. spectroscope >> spectroscopy 48. spectroscopy >> spectroscope 49. underclassman >> underclassmen 50. underclassmen >> underclassman 51. upperclassman >> upperclassmen 52. upperclassmen >> upperclassman done...
Wren
Using the Hamming Distance between two equal length strings which needs to be 1 here: <lang ecmascript>import "io" for File import "/fmt" for Fmt
var hammingDist = Fn.new { |s1, s2|
s1 = s1.toList // in case there are non-ASCII characters s2 = s2.toList // ditto var count = 0 var i = 0 while (i < s1.count) { if (s1[i] != s2[i]) { count = count + 1 if (count == 2) break // don't care about counts > 2 } i = i + 1 } return count
}
var wordList = "unixdict.txt" // local copy var words = File.read(wordList).trimEnd().split("\n").where { |w| w.count > 11 }.toList var count = 0 System.print("Changeable words in %(wordList):") for (word1 in words) {
for (word2 in words) { if (word1 != word2 && word1.count == word2.count) { if (hammingDist.call(word1, word2) == 1) { count = count + 1 Fmt.print("$2d: $-14s -> $s", count, word1, word2) } } }
}</lang>
- Output:
Changeable words in unixdict.txt: 1: aristotelean -> aristotelian 2: aristotelian -> aristotelean 3: claustrophobia -> claustrophobic 4: claustrophobic -> claustrophobia 5: committeeman -> committeemen 6: committeemen -> committeeman 7: committeewoman -> committeewomen 8: committeewomen -> committeewoman 9: complementary -> complimentary 10: complimentary -> complementary 11: confirmation -> conformation 12: conformation -> confirmation 13: congresswoman -> congresswomen 14: congresswomen -> congresswoman 15: councilwoman -> councilwomen 16: councilwomen -> councilwoman 17: craftsperson -> draftsperson 18: draftsperson -> craftsperson 19: eavesdropped -> eavesdropper 20: eavesdropper -> eavesdropped 21: frontiersman -> frontiersmen 22: frontiersmen -> frontiersman 23: handicraftsman -> handicraftsmen 24: handicraftsmen -> handicraftsman 25: incommutable -> incomputable 26: incomputable -> incommutable 27: installation -> instillation 28: instillation -> installation 29: kaleidescope -> kaleidoscope 30: kaleidoscope -> kaleidescope 31: neuroanatomy -> neuroanotomy 32: neuroanotomy -> neuroanatomy 33: newspaperman -> newspapermen 34: newspapermen -> newspaperman 35: nonagenarian -> nonogenarian 36: nonogenarian -> nonagenarian 37: onomatopoeia -> onomatopoeic 38: onomatopoeic -> onomatopoeia 39: philanthrope -> philanthropy 40: philanthropy -> philanthrope 41: prescription -> proscription 42: proscription -> prescription 43: schizophrenia -> schizophrenic 44: schizophrenic -> schizophrenia 45: shakespearean -> shakespearian 46: shakespearian -> shakespearean 47: spectroscope -> spectroscopy 48: spectroscopy -> spectroscope 49: underclassman -> underclassmen 50: underclassmen -> underclassman 51: upperclassman -> upperclassmen 52: upperclassmen -> upperclassman
XPL0
<lang XPL0>string 0; \use zero-terminated strings int Dict(26000); \pointers to words (enough for unixdict.txt) int DictSize; \actual number of pointers in Dict
func StrCmp(A, B); \Compare string A to B char A, B; \Returns: >0 if A>B, =0 if A=B, and <0 if A>1 do
[if A(I) # B(I) then return A(I) - B(I); if A(I) = 0 then return 0; ];
]; \StrCmp
func LookUp(Word); \Return 'true' if Word is in Dict char Word; int Lo, Hi, I, Cmp; [Lo:= 0; Hi:= DictSize-1; loop [I:= (Lo+Hi) / 2; \binary search
Cmp:= StrCmp(Word, Dict(I)); if Cmp < 0 then Hi:= I-1 else Lo:= I+1; if Cmp = 0 then return true; if Lo > Hi then return false; ];
]; \LookUp
int I, J, L, DI, Ch, Count; char Word, ChgWord(25); \longest word in unixdict.txt is 22 chars def Tab=$09, LF=$0A, CR=$0D, EOF=$1A;
[FSet(FOpen("unixdict.txt", 0), ^I); \load dictionary OpenI(3); \assume alphabetical order and all lowercase DI:= 0; \ignore non-alpha characters: 0..9, ' and & repeat Dict(DI):= Reserve(0); \get pointer to memory used to store Word
Word:= Dict(DI); I:= 0; loop [repeat Ch:= ChIn(3) until Ch # CR; \remove possible CR if Ch=LF or Ch=EOF then quit; Word(I):= Ch; I:= I+1; ]; Word(I):= 0; \terminate Word string I:= Reserve(I+1); \reserve memory used for Word DI:= DI+1; \next dictionary entry
until Ch = EOF; DictSize:= DI;
DI:= 0; Count:= 0; \print out all changeable words repeat Word:= Dict(DI); \assume all lowercase letters
I:= 0; J:= 0; loop [Ch:= Word(I); if Ch = 0 then quit; ChgWord(I):= Ch; I:= I+1; ]; ChgWord(I):= 0; if I >= 12 then \Word must have at least 12 chars [for J:= 0 to I-1 do \for all letter positions in Word [for L:= ^a to ^z do \for all letters if L # Word(J) then [ChgWord(J):= L; if LookUp(ChgWord) then [Count:= Count+1; IntOut(0, Count); ChOut(0, Tab); Text(0, Word); ChOut(0, Tab); Text(0, ChgWord); CrLf(0); ]; ChgWord(J):= Word(J); \undo letter change ]; ]; ]; DI:= DI+1;
until DI >= DictSize; ]</lang>
- Output:
1 aristotelean aristotelian 2 aristotelian aristotelean 3 claustrophobia claustrophobic 4 claustrophobic claustrophobia 5 committeeman committeemen 6 committeemen committeeman 7 committeewoman committeewomen 8 committeewomen committeewoman 9 complementary complimentary 10 complimentary complementary 11 confirmation conformation 12 conformation confirmation 13 congresswoman congresswomen 14 congresswomen congresswoman 15 councilwoman councilwomen 16 councilwomen councilwoman 17 craftsperson draftsperson 18 draftsperson craftsperson 19 eavesdropped eavesdropper 20 eavesdropper eavesdropped 21 frontiersman frontiersmen 22 frontiersmen frontiersman 23 handicraftsman handicraftsmen 24 handicraftsmen handicraftsman 25 incommutable incomputable 26 incomputable incommutable 27 installation instillation 28 instillation installation 29 kaleidescope kaleidoscope 30 kaleidoscope kaleidescope 31 neuroanatomy neuroanotomy 32 neuroanotomy neuroanatomy 33 newspaperman newspapermen 34 newspapermen newspaperman 35 nonagenarian nonogenarian 36 nonogenarian nonagenarian 37 onomatopoeia onomatopoeic 38 onomatopoeic onomatopoeia 39 philanthrope philanthropy 40 philanthropy philanthrope 41 prescription proscription 42 proscription prescription 43 schizophrenia schizophrenic 44 schizophrenic schizophrenia 45 shakespearean shakespearian 46 shakespearian shakespearean 47 spectroscope spectroscopy 48 spectroscopy spectroscope 49 underclassman underclassmen 50 underclassmen underclassman 51 upperclassman upperclassmen 52 upperclassmen upperclassman