Hi,
I came across a neat problem that I have been trying to code to improve my programming problem solving skills. It is quite simple but I am struggling to get my head around it. Any help would be much appreciated.
The problem is the following: a starting word, such as head, is transformed into a second word, such as tail, by way of changing a single letter at a time. All intermediate words must be in the dictionary. The aim is to find the fewest number of intermediate word to perform the transform.
e.g. transform long --> line
long -> lone
lone -> line
So far i copy the start word into a secondary array to manipulate it and place this word at the start of a linked list. Using a function I start with the first letter and loop through all combinations from a-z checking that each is a dictionary word. If it is a word I recursively call the function checking it is not in the list, then add it to the head of the list. I keep recursively changing letters and testing until I find the transformed word.
This method is kind of working, but not really and I think that I am missing something vital. I could do with a few pointers to get me in the right direction. Here is the code I have so far (please ignore the memory leaking :P ) -
/*
-
* find the shortest path to bread from short changing only one letter
-
* at a time
-
*/
-
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
-
struct words_t {
-
char word[6];
-
struct words_t *next;
-
};
-
-
int deep = 0;
-
-
int shuffle(struct words_t *head, char word[6], char target[6], int lp);
-
int dict(char word[6]);
-
int find(struct words_t *head, char word[6]);
-
int print(struct words_t *head);
-
-
char previous[BUFSIZ][5];
-
-
int
-
main(void) {
-
char start[6] = "short", end[6] = "chart";
-
struct words_t *words;
-
-
-
/* make the first list element the starting point */
-
words = malloc(sizeof(words));
-
strncpy(words->word, start, 6);
-
words->next = NULL;
-
-
shuffle(words, start, end, 0);
-
-
return 0;
-
}
-
-
-
int
-
shuffle(struct words_t *head, char word[6], char target[6], int lp) {
-
int i, letter;
-
char this_letter, current[6];
-
struct words_t *new;
-
-
if(find(head, word) == 1) {
-
/* a match was found for this word already */
-
return 0;
-
}
-
-
new = malloc(sizeof(new));
-
strncpy(new->word, word, 6);
-
strncpy(current, head->word, 6);
-
new->next = head;
-
head= new;
-
-
printf("%d==> %s\n", lp, new->word);
-
-
/* loop over all letters */
-
for(i=0; i<5; i++) {
-
-
/* remember the current letter */
-
this_letter = current[i];
-
-
/* increment letter */
-
for(letter='a'; letter<='z'; ++letter) {
-
if(letter == this_letter) ++letter;
-
current[i] = letter;
-
-
/* have we found a word in the dictionary? */
-
if(dict(current)) {
-
if(strncmp(current, target, 5) == 0) {
-
printf("\n\n\tFOUND IT!\n");
-
print(head);
-
exit(0);
-
}
-
shuffle(head, current, target, i);
-
}
-
}
-
-
/*
-
* a word was not found with this letter, revert to
-
* last word
-
*/
-
if(((letter-1) == 'z') && (! dict(current))) {
-
current[i] = this_letter;
-
}
-
}
-
}
-
-
-
int
-
find(struct words_t *head, char word[6]) {
-
struct words_t *current = head;
-
-
while(current->next != NULL) {
-
if(strncmp(current->word, word, 5) == 0)
-
/* this word has already been used return 1 */
-
return 1;
-
current = current->next;
-
}
-
return 0;
-
}
-
-
-
int
-
print(struct words_t *head) {
-
struct words_t *current = head;
-
-
while(current->next != NULL) {
-
printf("<%s> ", current->word);
-
current = current->next;
-
}
-
return 0;
-
}
-
-
-
int
-
dict(char word[6]) {
-
char grep[152];
-
-
snprintf(grep, 152, "grep --silent '^%s$' /usr/share/dict/words", word);
-
-
/* return 1 if a match is found */
-
return !system(grep);
-
}
-
6 2850 Banfa 9,065
Recognized Expert Moderator Expert
You have an error at line 52
new = malloc(sizeof(n ew));
This allocates 4 bytes to assign to a pointer to a 10 (probably but could be 14) structure. Try this
new = malloc(sizeof *new);
You have the same error at line 27.
You put the first word into the list twice, once at line 32 and once at line 53 on the first iteration. This also means that you have made find ignore the last word in the list when comparing to get round this starting corner case. However on every other iteration this is an error.
Pass an empty list to shuffle on the first iteration, fix find to search all entries.
Line 67
for(letter='a'; letter<='z'; ++letter) {
is platform defined behaviour, the C standard does not guarantee that 'a' + 1 == 'b' it is dependent on the execution character set that the platform uses.
Line 75 - 76
print(head);
exit(0);
Poor practice printing and exiting from the depths of a recursive function call, exit the recursive function to main and print in main. Also you do not release any of your allocated memory, also bad practice.
Thank you for pointing out the errors, I would have expected to get more errors not allocating memory properly but it seemed to work and is fixed now. Also the head of the list is fixed. I knew about the (extensive) memory leak I will fix that when I understand the algorithm.
About the algorithm, this method I am using seems to work. However, it is slow and won't always find the quickest path unless I search every word in the dictionary, I am just wondering if there is a better approach to it. Does anyone have any ideas I can try? I am not looking for code, just a hand with how best to solve the problem at hand.
Thanks
David
donbock 2,426
Recognized Expert Top Contributor
How many times do you call dict in an average pass? It might be worth the overhead to organize the dictionary into a hash or tree so you can more quickly determine if a letter-pattern is a dictionary word.
Banfa 9,065
Recognized Expert Moderator Expert
Also how big is the dictionary, can you cache some of it in memory?
I thought about it a bit and changed my algorithm. Now I check every word that is one letter different from the current word instead of following every path to the end. Each letter change is a new level and I move down the level checking all new words against the target. When the target is found this is the shortest path.
I could put the dictionary into a hash and speed it up, but I am more interested in the algorithm.
Thanks :) -
-
/*
-
* find the shortest path to bread from short changing only one letter
-
* at a time
-
*/
-
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
-
#define WORDSIZ 5
-
-
struct words_t {
-
int level;
-
char word[WORDSIZ+1];
-
struct words_t *next;
-
};
-
-
int shuffle(struct words_t **head, struct words_t *level, char target[WORDSIZ+1], int lp);
-
int dict(char word[WORDSIZ+1]);
-
int find(struct words_t *head, char word[WORDSIZ+1]);
-
int print(struct words_t *head);
-
-
-
int
-
main(void) {
-
int counter;
-
char start[WORDSIZ+1] = "short", end[WORDSIZ+1] = "bread";
-
struct words_t *head;
-
-
printf("START: %s\tTARGET:%s\n", start, end);
-
-
-
head = malloc(sizeof(*head));
-
head->level = 0;
-
strncpy(head->word, start, WORDSIZ+1);
-
head->next = NULL;
-
-
counter = shuffle(&head, head, end, 0);
-
-
printf("\n\n\tFOUND IT!\n");
-
printf("count %d\n", counter);
-
print(head);
-
-
return 0;
-
}
-
-
-
int
-
shuffle(struct words_t **head, struct words_t *level, char target[WORDSIZ+1], int lp) {
-
static int counter = 0;
-
int i, j;
-
char current[WORDSIZ+1];
-
const char alpha[27] = "abcdefghijklmnopqrstuvwxyz";
-
struct words_t *currlist = *head;
-
struct words_t *nextlist = currlist;
-
struct words_t *new;
-
-
/* go to the next level of words */
-
++lp;
-
printf("level: %d\n", lp);
-
-
/* search through current level list of words */
-
while((currlist != NULL) && (currlist->level == (lp-1))) {
-
-
strncpy(current, currlist->word, WORDSIZ+1);
-
-
/* increment letter */
-
for(j=0; j<26; ++j) {
-
-
/* loop over all letters */
-
for(i=0; i<WORDSIZ; i++) {
-
-
/* try next letter */
-
if(alpha[j] == currlist->word[i]) ++i;
-
current[i] = alpha[j];
-
-
/* have we found a word in the dictionary? */
-
if(dict(current)) {
-
-
if(find(*head, current) == 1) {
-
/* a match was found for this word already */
-
break;
-
}
-
-
new = malloc(sizeof(*new));
-
strncpy(new->word, current, WORDSIZ+1);
-
new->next = nextlist;
-
new->level = lp;
-
strncpy(new->word, current, WORDSIZ+1);
-
nextlist = new;
-
*head = nextlist;
-
-
-
/* end recursion here */
-
if(strncmp(current, target, WORDSIZ) == 0) {
-
/* the target word is found */
-
return 1;
-
}
-
}
-
-
/*
-
* a word was not found with this letter, revert to
-
* last word
-
*/
-
current[i] = currlist->word[i];
-
}
-
}
-
currlist = currlist->next;
-
}
-
-
/* current word matches the target */
-
if((counter = shuffle(head, *head, target, lp))) {
-
return ++counter;
-
}
-
-
/* only unused words reach here and are freed */
-
//free(new);
-
return 0;
-
}
-
-
-
int
-
find(struct words_t *head, char word[WORDSIZ+1]) {
-
struct words_t *current = head;
-
-
while(current != NULL) {
-
if(strncmp(current->word, word, WORDSIZ) == 0)
-
/* this word has already been used return 1 */
-
return 1;
-
current = current->next;
-
}
-
return 0;
-
}
-
-
-
int
-
print(struct words_t *head) {
-
struct words_t *current = head;
-
-
if(current == NULL) return 0;
-
-
/* recursively print to get correct order */
-
print(current->next);
-
printf("<%d:%s> ", current->level, current->word);
-
-
return 0;
-
}
-
-
-
int
-
dict(char word[WORDSIZ+1]) {
-
char grep[152];
-
-
snprintf(grep, 151, "grep --silent '^%s$' /usr/share/dict/words", word);
-
-
/* return 1 if a match is found */
-
return !system(grep);
-
}
-
I will describe the algorithm and the computer program in Standard C language to solve this word puzzle.
The goal is to order all the words in the dictionary in a tree like structure starting from the 'start' word .
This will be the trunk of the tree or level 0. Then the program iterates through the dictionary (a linked list of structures) and discovers all words with a single character difference. My procedure compares the words by comparing the characters instead of iterating the alphabet - less expensive. This will be our 1 level of the branches of the tree. Then for each word on level 1 and following levels the program iterates again through the dictionary, discovers words with single character difference and associates them (using C pointers) to the parent word. On the upper branches many words will point to a single parent word. Every time during the iteration the program checks if the targeted word was reached and if so, stops and prints the words in order. Once a word is associated on the tree it will be ignored in following iterations. The reason for ignoring the words already included on the tree is that if we don't it, then logically we are going in circles.
The dictionary is initialized from the beginning in the only linked list used in the program.
The word structure is like this:
struct words_t {
int level;
char word[WORDSIZE+1];
struct words_t *parent;
struct words_t *next;
};
pointer 'next' is for navigating the dictionary and pointer 'parent' is for constructing the tree like structure which will hold the result.
here it is:
NB the dictionary is truncated, but I have attached the full header file here too.
puzzle.h -
#define WORDSIZE 4
-
-
struct words_t {
-
int level;
-
char word[WORDSIZE+1];
-
struct words_t *parent;
-
struct words_t *next;
-
};
-
-
int find(struct words_t *currword, struct words_t *dicstart, struct words_t *target, int level);
-
int print(struct words_t *path);
-
-
-
char *dictionary[] = {
-
"abet",
-
"able",
-
"ably",
-
"abut",
-
"aces",
-
"ache",
-
"acid",
-
"acme",
-
"acne",
-
"acre",
-
"acts",
-
"adds",
-
"adze",
-
"aeon",
-
"afar",
-
"aged",
-
..........
-
}
-
and
puzzle.c -
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
#include <ctype.h>
-
#include "puzzle.h"
-
-
int main(int argc, char *argv[])
-
{
-
-
if (( argc != 3 ) || ( strlen(argv[1]) !=4 ) || (strlen(argv[2]) != 4))
-
{
-
puts("Usage: puzzle <(4char)From Word> <(4char)To Word>");
-
return(-1);
-
}
-
-
char arg1[WORDSIZE+1];
-
char arg2[WORDSIZE+1];
-
int i;
-
//To lower case
-
for (i=0;i<= WORDSIZE;i++){
-
arg1[i]=tolower(argv[1][i]);
-
arg2[i]=tolower(argv[2][i]);
-
}
-
-
-
printf("START: %s\tTARGET: %s\n",arg1, arg2);
-
-
struct words_t *start = NULL;
-
struct words_t *end = NULL;
-
struct words_t *dict = NULL;
-
struct words_t *new = NULL;
-
-
-
//Initialize dictionary
-
-
i= 0;
-
while (i < 1989){
-
new = malloc(sizeof(*new));
-
new->level = 1000000;
-
strncpy(new->word, dictionary[i], WORDSIZE+1);
-
new->parent = NULL; //pointer to the root of the treei (initialized as NULL)
-
new->next = dict; //pointer to the next word in the dictionary
-
dict = new;
-
i++;
-
}
-
//My start word goes here and next is the dictionary address:
-
start = malloc(sizeof(*start));
-
start->level = 0;
-
strncpy(start->word, arg1, WORDSIZE+1);
-
start->parent = NULL; //this pointer will remain NULL as it is the root of the tree
-
start->next = dict; //pointer to the first word from the dictionary
-
//The end (target word goes here)
-
end = malloc(sizeof(*end));
-
end->level = 1000000;
-
strncpy(end->word, arg2, WORDSIZE+1);
-
end->parent = NULL;
-
end->next = NULL;
-
-
-
-
static int levelcounter = 0;
-
struct words_t *cmpword = NULL;
-
-
for (levelcounter=0;levelcounter<1000; levelcounter++){
-
cmpword = start;
-
while (cmpword != NULL){
-
//printf("\nCHEKPOINT%d\n",cmpword->level);
-
if (cmpword->level == levelcounter){
-
if(find(cmpword,start,end,levelcounter+1))
-
{
-
printf("\n\n\tShortest path found on step:%d\n",levelcounter+1);
-
return 0;
-
}
-
}
-
cmpword = cmpword->next;
-
}
-
}
-
printf("\n\n\tPath was not found till step:%d\n",levelcounter);
-
return 0;
-
}
-
-
-
-
int find(struct words_t *currword, struct words_t *dicstart, struct words_t *target, int level) {
-
-
struct words_t *dicnext = dicstart->next;
-
-
while(dicnext != NULL) {
-
//Find a word that is a single character different from the start word and has not been used already
-
//Words which have been attached to the tree have level != 1000000
-
if ((dicnext->level == 1000000) &&
-
(((currword->word[0] != dicnext->word[0]) && (currword->word[1] == dicnext->word[1]) && (currword->word[2] == dicnext->word[2]) && (currword->word[3] == dicnext->word[3])) ||
-
((currword->word[0] == dicnext->word[0]) && (currword->word[1] != dicnext->word[1]) && (currword->word[2] == dicnext->word[2]) && (currword->word[3] == dicnext->word[3])) ||
-
((currword->word[0] == dicnext->word[0]) && (currword->word[1] == dicnext->word[1]) && (currword->word[2] != dicnext->word[2]) && (currword->word[3] == dicnext->word[3])) ||
-
((currword->word[0] == dicnext->word[0]) && (currword->word[1] == dicnext->word[1]) && (currword->word[2] == dicnext->word[2]) && (currword->word[3] != dicnext->word[3]))))
-
{
-
-
//printf("(level:%d %s)\n",level,dicnext->word);
-
dicnext->parent = currword; //word with a single char difference is found and attached to the tree
-
dicnext->level = level; //tagging this word with the current level
-
-
//Check if this is our targeted last word
-
if(strncmp(dicnext->word, target->word, WORDSIZE) == 0)
-
{
-
//if it is the targeted word, print the chain and return 1
-
print(dicnext);
-
return 1;
-
}
-
}
-
-
dicnext = dicnext->next;
-
-
}
-
return 0;
-
}
-
-
-
-
-
-
int print(struct words_t *path) {
-
struct words_t *current = path;
-
//struct words_t *prev;
-
if(current == NULL) return 0;
-
/* recursively print to get correct order */
-
print(current->parent);
-
printf("<%d:%s>\n", current->level, current->word);
-
return 0;
-
}
-
-
-
Regards,
Plamen
Sign in to post your reply or Sign up for a free account.
Similar topics |
by: |
last post by:
A mail sent by you has been identified as suspicious by MailMonitor for Exchange.
Event: infection
Action: Message quarantined
Message ID: <GANDHSVRIpMozAb6jNo00000095@gandhsvr.GouldHarrison.local>
Message subject: Mail System Error - Returned Mail
Recipient: "sales@gouldharrison.co.uk" <sales@gouldharrison.co.uk>
=============================================================
|
by: Bonj |
last post by:
I was in need of an encryption algorithm to the following requirements:
1) Must be capable of encrypting strings to a byte array, and decyrpting
back again to the same string
2) Must have the same algorithm work with strings that may or may not be
unicode
3) Number of bytes back must either be <= number of _TCHARs in *
sizeof(_TCHAR), or the relation between output size and input size can be
calculated simply. Has to take into account the...
|
by: Ben Fidge |
last post by:
Hi
I'm working on a site which requires the users to specify a hotel at which
they're staying in London. The complete list of hotels comes to something
like 1600 records. Each record consists of Hotel Name, Street Address and
Postcode.
We need to make it as simple as possible for users to pick their hotel, but
I don't want to put 1600 hotel names in a drop-down list, and we have to
consider the fact that not every user is going to...
|
by: meenasamy |
last post by:
Hi all, i need to create a function that takes three parameters(
Original currency, needed currency, amount) i need to convert from the
original currency to the needed currency the amount and return the new
amount in the needed currency, I need this function to work real time
(obtaining real time currency exchange rates), any ideas???
|
by: almurph |
last post by:
Hi everyone,
I'm looking around for a VB.NET algorithm that can do non-exact
matches, that is, a "looks alike" type logic for word patterns.
Does anyone have any suggestions/comments/algorthms they would like
to mention? Any comments/suggestions greatly appreciated.
Al.
| |
by: Protoman |
last post by:
How would I write a user defnable letter swapping algorithm? I've
written an Enigma encrypting program, and I need to redo the plugboard
function. Right now, the letter swapping is fixed. I need to allow the
user to swap 23 pairs of characters. As you may have guessed, I'm using
an alphabet of 36 characters, A-Z and 0-9. Thanks!!!!
|
by: Gigs_ |
last post by:
Hi all!
I have text file (english-croatian dictionary) with words in it in alphabetical
order.
This file contains 179999 words in this format:
english word: croatian word
I want to make instant search for my gui
Instant search, i mean that my program search words and show words to user as
user type letters.
|
by: Ulterior |
last post by:
Hi, everyone,
I have a simple problem, which bothers me for some while. I will try
to explain it -
There is some string, whith different letters in it. Is it possible to
analyse this string and the
reffer to it knowing that it has some certain letters in it using only
some integer value?
|
by: pj |
last post by:
Hi,
I 'm currently writing a program that performs transliteration (i.e.,
converts greek text written using the english alphabet to "pure" greek
text using the greek alphabet) as part of my thesis. I have decided to
add the capability to convert words using some sort of lookup
algorithm as a sidekick to the "normal" conversion algorithm, and here
it starts getting interesting. I want to find an algorithm that
satisfies these...
|
by: kobe67 |
last post by:
Create a program that will ask a string from the user, your program should automatically exchange the 1st & the last letter of every word in the string
|
by: marktang |
last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look !
Part I. Meaning of...
| |
by: Hystou |
last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it.
First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
|
by: Oralloy |
last post by:
Hello folks,
I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>".
The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed.
This is as boiled down as I can make it.
Here is my compilation command:
g++-12 -std=c++20 -Wnarrowing bit_field.cpp
Here is the code in...
|
by: jinu1996 |
last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth.
The Art of Business Website Design
Your website is...
|
by: tracyyun |
last post by:
Dear forum friends,
With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
|
by: isladogs |
last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM).
In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules.
He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms.
Adolph will...
|
by: conductexam |
last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one.
At the time of converting from word file to html my equations which are in the word document file was convert into image.
Globals.ThisAddIn.Application.ActiveDocument.Select();...
| |
by: TSSRALBI |
last post by:
Hello
I'm a network technician in training and I need your help.
I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs.
The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols.
I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
|
by: adsilva |
last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
| |