I am trying to design a recursive solution to Lewis Carroll's 'Word Ladder' game
The game attempts to link a starting word, for example 'WET' to an ending word, e.g. 'DRY' using a chain of words where between any 2 words just 1 letter is different. There is a limit on how many words can be used to make the chain.
For example for WET - DRY the solution is WET - bet - bat - bay - day - DRY
I am using the below recursive C function
exit condition: word is 1 step away from the 'target word' - i.e. if it sees DAY (= 1 step away from DRY), it returns true
It finds the solution, however, the problem is: it does not pass the solution back to original function. With each return call, the chain shaves off one word - i.e. the function's innermost call correctly solves 'bet - bat - bay - day' and stores it in the answer_chain - however once calls unwind - this array somehow gets passed as 'bet - bat - bay' (=loses 1 word), and so on, and as a result the original outermost function call gets no information about the solution.
I've tried:
Copying 'by value' by creating a c-style string first, assigning it back later
Copying the array item-by-item only when the first return is reached, and just assigning one array to the other otherwise (answer_chain = answer_chain_temp)
I've unfortunately not been able to figure out what the issue actually is.
Any help would be greatly appreciated.
bool find_chain(const char* start_word, const char* target_word, const char* answer_chain[], int max_steps){
if (answer_chain[0] == NULL) {} // This is to zero out incoming answer_chain
else if (!valid_step(start_word,answer_chain[0])){
delete[] answer_chain;
for (unsigned int i = 0; i < sizeof(answer_chain)/sizeof(answer_chain[0]);++i){
answer_chain[i] = NULL;
}
}
int filled_words = find_sz_of_char_arrays(answer_chain); // Looks for null-terminator
string previous_word, try_new_word;
bool is_solved = false;
if (valid_chain(answer_chain) && valid_step(answer_chain[filled_words-1],target_word) ) {
is_solved = true;
}
if (is_solved) {return true;}
if (max_steps == 0 && !is_solved) {return false;}
if (filled_words > 0) { previous_word = answer_chain[filled_words-1];} else {previous_word = start_word;}
for (unsigned int j = 0; j < strlen(start_word); ++j){
try_new_word = previous_word;
for (unsigned int i = 0; i < 26; ++i){
char new_char = i + 'A';
if (try_new_word.at(j) != new_char) { // has to be different from character already in place
try_new_word.at(j) = new_char;
if (valid_step(previous_word.c_str(),try_new_word.c_str()) && !is_word_already_in_chain(try_new_word.c_str(),answer_chain) ) {
const char** answer_chain_temp = new const char*[15]; // append 'try' word to answer array
for (int k = 0; k < filled_words;++k){
answer_chain_temp[k] = answer_chain[k];}
answer_chain_temp[filled_words] = try_new_word.c_str();
answer_chain_temp[filled_words+1] = NULL;
if (find_chain(start_word,target_word,answer_chain_temp,max_steps-1)){
delete[] answer_chain;
answer_chain = new const char*[15];
for (int kk = 0; kk < 15;++kk) {
if (answer_chain_temp[kk]!=NULL){
answer_chain[kk] = answer_chain_temp[kk];
}
}
delete[] answer_chain_temp;
return true;
} // if successful - append the word
} // if valid step
} // if letter is differerent
} // for i
} // for j
return false;
}
EDIT:
I've now changed the middle part to copy the .s_str() by value, however the issue still seems to persist. I believe something is off with how I am copying and deleting after the solution has been found:
const char** answer_chain_temp = new const char*[15]; // append 'try' word to answer array
for (int k = 0; k < filled_words;++k){answer_chain_temp[k] = answer_chain[k];}
char * writable = new char[try_new_word.size() + 1];
std::copy(try_new_word.begin(), try_new_word.end(), writable);
writable[try_new_word.size()] = '\0';
answer_chain_temp[filled_words] = writable;
answer_chain_temp[filled_words+1] = NULL;
if (find_chain(start_word,target_word,answer_chain_temp,max_steps-1)){
delete[] answer_chain;
answer_chain = new const char*[15];
for (int kk = 0; kk < 15;++kk) {
if (answer_chain_temp[kk] != NULL){
answer_chain[kk] = answer_chain_temp[kk]; // <<<<<< I believe the problem is here
}
}
display_chain(answer_chain,cout); cout << endl;
delete[] answer_chain_temp;
return true;```
Related
I'm trying to do an interesting exercise from a book with the following question:
For our dynamically allocated strings, create a function replaceString that takes
three parameters, each of type arrayString: source, target, and replaceText.
The function replaces every occurrence of target in source with replaceText.
For example, if source points to an array containing abcdabee, target points to
ab, and replaceText points to xyz, then when the function ends, source should
point to an array containing xyzcdxyzee.
I replace every occurence of target, in this case ab, with replaceText, in this case xyz in char *.
For this exercise the book uses a 0 to mark char * termination and uses a typedef for char *. I've written the following function to test replaceString out.
arrayString being typedef char *arrayString
void replaceStringTester() {
arrayString test = new char[8];
test[0] = 'a';
test[1] = 'b';
test[2] = 'c';
test[3] = 'd';
test[4] = 'a';
test[5] = 'b';
test[6] = 'e';
test[7] = 'e';
test[8] = 0;
arrayString target = new char[3];
target[0] = 'a';
target[1] = 'b';
target[3] = 0;
arrayString replaceText = new char[4];
replaceText[0] = 'x';
replaceText[1] = 'y';
replaceText[2] = 'z';
replaceText[3] = 0;
replaceString(test, target, replaceText);
cout << test;
}
As well as the following function to find the length of an arrayString
int length(arrayString as) {
int count = 0;
while (as[count] != 0) {
++count;
}
return count;
}
My idea up until now has been, iterate over the source arrayString, check does the source start with the first char of target? If it does, iterate over target and check if the rest lines up. I've written following function for it, however im uncertain if it does exactly what I drafted up.
void replaceString(arrayString &source, arrayString target, arrayString replaceText) {
int sourceLength = length(source);
int targetLength = length(target);
int replaceTextLength = length(replaceText);
int targetPresent = 0;
for (int i = 0; i < sourceLength; ++i) {
if (source[i] == target[0]) {
int count = 0;
for (int k = 0; k < targetLength; ++k) {
if (target[k] == source[i + k]) {
++count;
}
}
if (count == targetLength) {
++targetPresent;
}
}
}
int newStringLength = sourceLength + (replaceTextLength - targetLength) * targetPresent + 1;
arrayString newString = new char[newStringLength];
newString[newStringLength] = 0;
int j = 0;
int i = 0;
while (j < newStringLength) {
if (source[j] == target[0]) {
bool targetAcquired = false;
for (int k = 0; k < targetLength; ++k) {
if (target[k] == source[j + k]) {
targetAcquired = true;
} else {
targetAcquired = false;
break;
}
}
if (targetAcquired) {
for(int k = 0; k < replaceTextLength; ++k) {
newString[i] = replaceText[k];
++i;
}
j += targetLength;
}
if (!targetAcquired) {
newString[i] = source[j];
++j;
++i;
}
} else {
newString[i] = source[j];
++j;
++i;
}
}
delete[] source;
source = newString;
}
Edit:
I've solved it by implementing two trackers for our positions in each respective stringArray and then filtering with a boolean what is in where. Thank you for your help.
For starters using this typedef
typedef char *arrayString;
is a bad idea. For example if you need to declare a pointer to a constant string then this declaration
const arrayString p;
will not denote
const char *p;
It means
char * const p;
that is not the same as the above declaration..
And the second and the third parameters of your function should be declared as having the type const char * because they are not changed within the function,
The function should be declared the following way
char * replaceString( char * &source, const char *target, const char *replaceText );
Within the function you need at first to count how many times the string target is found in the string source. When using this information and the length of the string replaceText you need to allocate dynamically a new character array if it is required. When just copy the source string into the dynamically allocated array substituting the target string for the replacing string.
After that you should delete the source string and assign its pointer with the newly formed string.
Pat attention to that there is standard C string function strstr declared in the header <cstring> that can simplify your code. Also to find the length of a string you can use another standard C string function strlen. Otherwise you should write them yourself.
Count how many elements you've assigned:
test[0] = 'a'; // 1
test[1] = 'b'; // 2
test[2] = 'c'; // 3
test[3] = 'd'; // 4
test[4] = 'a'; // 5
test[5] = 'b'; // 6
test[6] = 'e'; // 7
test[7] = 'e'; // 8
test[8] = 0; // 9
You've assigned 9 elements. Are there 9 elements in the array?
arrayString test = new char[8];
No. There aren't 9 elements in the array. There are 8. You access outside the bounds of the array and the behaviour of the program is undefined.
arrayString being typedef char *arrayString
Don't use obfuscation like this. It reduces readability and provides no advantages.
Fairly new to coding. Trying some of the easy projects at LeetCode, and failing... Ha! I am trying to take an integer and convert it to a string so I can reverse it, then re-convert the reversed string back into a integer.
This code is throwing the "terminate after throwing and instance of 'std::invalid argument' what(): stoi" error. I've spent an hour searching google and other questions here on SO, but can't figure out why it's not working.
bool isPalindrome(int x) {
std::string backwards ="";
std::string NumString = std::to_string(x);
for (int i = NumString.size(); i >= 0 ; i--) {
backwards += NumString[i];
}
int check = std::stoi(backwards);
if (check == x) {
return true;
}
else {
return false;
}
}
EDIT: I think I figured it out. It was adding the null character to the end of the string upon first conversion, then adding it to the beginning of the string when I reversed it. Spaces can't be converted to integers.
So... I changed this line and it works:
for (int i = NumString.size() - 1; i >= 0 ; i--)
you can also reverse number without using string.
bool isPalindrome(int x) {
long long rev = 0;
int cur = x;
while( cur > 0) {
rev *= 10;
rev += cur % 10;
cur /=10;
}
return rev == x;
}
Its simpler than your answer that you edited in. YOu have
for (int i = NumString.size(); i >= 0 ; i--) {
backwards += NumString[i];
}
Imagine that Numstring has length 3 (no matter what spaces, digits,....)
So now you are efectively doing
for (int i = 3; i >= 0 ; i--) {
backwards += NumString[i];
}
So first loop goes
backwards += NumString[3];
well the indexes of things in an array of length 3 in c++ are 0,1,2. YOu are going one off the end
This is why you see loops doing
for(int i = 0; i < len; i++){}
Note the i < len not i <= len
Given two strings, write a method to decide if one is an anagram/permutation of the other. This is my approach:
I wrote this function to check if 2 strings are anagrams (such as dog and god).
In ascii, a to z is 97 - 122.
Basically I have an array of bools that are all initially false. Everytime I encounter a char in string1, it marks it as true.
To check if its an anagram, I check if any chars of string2 are false (should be true if encountered in string1).
I'm not sure how but this works too: arr[num] = true; (shouldnt work because I dont take into account that ascii starts at 97 and thus goes out of bounds).
(Side note: is there a better approach than mine?)
EDIT: Thanks for all the responses! Will be carefully reading each one. By the way: not an assignment. This is a problem from a coding interview practice book
bool permutation(const string &str1, const string &str2)
{
// Cannot be anagrams if sizes are different
if (str1.size() != str2.size())
return false;
bool arr[25] = { false };
for (int i = 0; i < str1.size(); i++) // string 1
{
char ch = (char)tolower(str1[i]); // convert each char to lower
int num = ch; // get ascii
arr[num-97] = true;
}
for (int i = 0; i < str2.size(); i++) // string 2
{
char ch = (char)tolower(str2[i]); // convert char to lower
int num = ch; // get ascii
if (arr[num-97] == false)
return false;
}
return true;
}
There is nothing inherent in C++ arrays that prevents you from writing beyond the end of them. But, in doing so, you violate the contract you have with the compiler and it is therefore free to do what it wishes (undefined behaviour).
You can get bounds checking on "arrays" by using the vector class, if that's what you need.
As for a better approach, it's probably better if your array is big enough to cover every possible character (so you don't have to worry about bounds checking) and it shouldn't so much be a truth value as a count, so as to handle duplicate characters within the strings. If it's just a truth value, then here and her would be considered anagrams.
Even though you state it's not an assignment, you'll still learn more if you implement it yourself, so it's pseudo-code only from me. The basic idea would be:
def isAnagram (str1, str2):
# Different lengths means no anagram.
if len(str1) not equal to len(str2):
return false
# Initialise character counts to zero.
create array[0..255] (assumes 8-bit char)
for each index 0..255:
set count[index] to zero
# Add 1 for all characters in string 1.
for each char in string1:
increment array[char]
# Subtract 1 for all characters in string 2.
for each char in string2:
decrement array[char]
# Counts will be all zero for an anagram.
for each index 0..255:
if count[index] not equal to 0:
return false
return true
Working approach : with zero additional cost.
bool permutation(const std::string &str1, const std::string &str2)
{
// Cannot be anagrams if sizes are different
if (str1.size() != str2.size())
return false;
int arr[25] = {0 };
for (int i = 0; i < str1.size(); i++) // string 1
{
char ch = (char)tolower(str1[i]); // convert each char to lower
int num = ch; // get ascii
arr[num-97] = arr[num-97] + 1 ;
}
for (int i = 0; i < str2.size(); i++) // string 2
{
char ch = (char)tolower(str2[i]); // convert char to lower
int num = ch; // get ascii
arr[num-97] = arr[num-97] - 1 ;
}
for (int i =0; i< 25; i++) {
if (arr[i] != 0) {
return false;
}
}
return true;
}
Yes, C and C++ both doesn't carry out the index-out-of-bounds.
It is the duty of the programmer to make sure that the program logic doesn't cross the legitimate limits. It is the programmer who need to make checks for the violations.
Improved Code:
bool permutation(const string &str1, const string &str2)
{
// Cannot be anagrams if sizes are different
if (str1.size() != str2.size())
return false;
int arr[25] = { 0 }; //<-------- Changed
for (int i = 0; i < str1.size(); i++) // string 1
{
char ch = (char)tolower(str1[i]); // convert each char to lower
int num = ch; // get ascii
arr[num-97] += 1; //<-------- Changed
}
for (int i = 0; i < str2.size(); i++) // string 2
{
char ch = (char)tolower(str2[i]); // convert char to lower
int num = ch; // get ascii
arr[num-97] = arr[num-97] - 1 ; //<-------- Changed
}
for (int i =0; i< 25; i++) { //<-------- Changed
if (arr[i] != 0) { //<-------- Changed
return false; //<-------- Changed
}
}
return true;
}
I have a C++ function that splits a char array into multiple char arrays when it encounters a delimiter. For some reason, when saving the third split array the program just crashes and sometimes returns an std::bad_alloc exception.
char ** explode(const char * arr, const char delim) {
int start, end, curr=0, count=1;
char ** strings;
//Iegūst explodēto stringu skaitu
for (int i = 0; arr[i] != 0; i++) {
if (arr[i] == delim && i != 0 && arr[i+1] != 0 && arr[i+1] != delim ) { //Nav pirmais, nav pēdējais, nav pa labi vēlviens delimiters
count++;
}
}
strings = new char*[count];
start = 0;
for (int i = 0; arr[i] != 0; i++) {
if (arr[i] == delim || arr[i+1] == 0) {
if (arr[i] == delim) {
end = i;
} else {
end = i+1;
}
if (end-start < 1) {
start++;
} else {
copystring(arr,strings[curr++],start,end-start);
start = i+1;
}
}
}
for (int i = 0; i < count; i++) {
cout << strings[i] << endl;
}
return strings;
}
//Pārkopē daļu no pirmā char masīva uz otru, no START pozīcijas, līdz GARUMS garumā
void copystring(const char * from, char *& to, const int start, const int garums) {
int curr=0;
if (garums < 1 || start > charlen(from)) {
return;
}
to = new char[garums];
for (int i = start; i < start+garums && from[i] != 0; i++) {
to[curr++] = from[i];
}
to[curr] = 0;
}
It's hard to tell because it doesn't really tell me at which line everything goes wrong, but I think it happens at
to = new char[garums];
I've tried debugging this line within CodeBlocks, but for some reason when using breakpoints and tracking the variables the applications works fine and executes correctly. It only crashes when running it normally, without debugging...
Also note, that I can't use strings or pretty much any library except fstream and iostream.
EDIT: I tried changing the new char[garums] part to new char[100] and it magically started working. The problem is that I then changed it to new char[10] in which case everything still worked. I even outputted the saved text to the console and it saved everything properly. How could it have saved big words in a char array that is 10 character long (the words I'm testing are longer than 10 characters)? When I changed it to new char[1] however it started crashing again, but again only after the 3rd loop iteration. So it somehow saved the first 2 words in a 1 character long array?
EDIT2: And now it magically started working even with new char[garums]. Something is really wrong here, anyone have any ideas?
The bug you refer to in your question likely crops up when trying to use the pointer
to pointer being returned from the explode function.
Some pointers ; If you have to write C code, don't use a mishmash of C/C++,
Use the library functions rather than re-inventing the wheel (strncpy in copystring)
Your word count was off because you didn't take into account the word between
the last delimiter and EOL
Below are some minor changes to your code as a complete example :
#include <stdio.h>
#include <strings.h>
void copystring(const char *from, char **to, const int numchars)
{
if (numchars > 0) {
*to = new char[numchars];
strncpy(*to, from, numchars) ;
(*to)[numchars] = '\0' ;
}
}
char **explode(const char * buffer, const char delim)
{
int count = 0 ;
if (strlen(buffer) > 0) {
int inword = 0 ;
int idx = 0 ;
do {
if (buffer[idx] == delim || buffer[idx] == '\0') {
if (inword == 1) {
count++ ;
inword = 0 ;
}
} else {
inword = 1 ;
}
} while (buffer[idx++] != 0) ;
}
int start = 0;
int end = 0 ;
int curr = 0 ;
int idx = 0 ;
char **values = new char*[count+1];
do {
if (buffer[idx] == delim || buffer[idx] == '\0') {
end = idx;
if (end-start > 0) {
copystring(&buffer[start], &values[curr++], end - start) ;
}
start = ++end ;
}
} while (buffer[idx++] != 0) ;
values[curr] = NULL ;
for (int idx = 0; idx < count; idx++) {
fprintf(stdout, "'%s'\n", values[idx]) ;
}
return values;
}
int main(int argc, char *argv[])
{
char inputstr[] = "The, quick, brown, fox, jumped, over,,, ,,, ,,,,, ,,,, the, lazy, dog's, back" ;
char **values = explode(inputstr, ',') ;
while (*values != NULL) {
fprintf(stdout, "%s\n" , *values) ;
*values++ ;
}
return (0) ;
}
Since I don't know what input data you have I will have to guess:
Here you allocate your pointer array but please note that all the pointers are uninitialized.
strings = new char*[count]
then when you parse the code you use a variable curr which you let run freely so it is not certain that all strings[] have been set to some value or whether curr lands on a number larger than count.
If I were you I would put in a check to make sure that:
a) curr does not exceed count
b) that if curr lands on a value less than count, set the rest of the pointers to nullptr
This has probably to do with to being of type char*& instead of type char*.
On the other hand, I never programmed C++ like this (are you sure that this is not C?). Using explicit memory management (like ´new´) is as good as playing playing russian roulette.
Here is a more standard C++ way of doing this:
#include <vector>
#include <string>
#include <iostream>
std::vector<std::string> splitString(std::string& str, char c) {
std::vector<std::string> substrings;
while(true) {
unsigned pos = str.find(c);
substrings.push_back(str.substr(0,pos));
if(pos == std::string::npos) break;
str = str.substr(pos+1);
}
return substrings;
}
int main()
{
char c = '*';
std::string str = "Some*string*that we need to split*properly*";
std::vector<std::string> result = splitString(str,c);
for(unsigned i = 0; i < result.size(); ++i) {
std::cout << i << ": " << result[i] << "\n";
}
}
Output:
0: Some
1: string
2: that we need to split
3: properly
4:
I am trying to determine if my code is a palindrome so I created a reverse function and then a palindrome function. I am trying to assign the reversed Character array into the new function but I can't seem to get it to compile.... any tips?
Here is my palindrome function
bool Palindrome(char Characters[], unsigned long length)
{
char tempstring[62];
tempstring[62] == reverse(Characters);
for(int i=0; i <= length; i++){
if(Characters[i] == tempstring[i])
return false;
else
return true;
}
}
Here is my reverse function
void reverse(char Characters[], unsigned long length)
{
char temp;
for(int i=0; i<length/2; i++){
temp = Characters[i];
Characters[i]=Characters[length-i-1];
Characters[length-i-1]=temp;
}
}
First things first, you have a typo; == is a compare equality, =. You ought to have written
tempstring[62] = reverse(Characters);
But this will still not work. For starters, reverse is a void function and so therefore it does not return a value.
The quickest fix will be to replace that line with
reverse(Characters, length);
(Note that I'm also passing the length parameter as required).
One final thing: if you have organised your file so that reverse appears after Palindrome, then you need to forward declare reverse using this statement:
void reverse(char Characters[], unsigned long length);
That fixes the compilation errors. I defer to you to check the runtime behaviour.
You are making this quite complicated.
Just find the end of the string (strlen). Read from both ends a character at a time and if they do not match then it is not a palindrome. If the indexes become the same or they cross then you are done. It is indeed a palindrome.
I.e
bool Palindrome(char *s) {
int left = 0, right = strlen(s) - 1;
while (left < right) {
if (s[left] != s[right] return false;
++left;
--right;
}
return true;
}
EDIT
Similar vain to construct the reverse
char *Reverse(char *s)
{
char *rev = new char[strlen(s) + 1];
int left = 0, right = strlen(s) - 1;
while (right > -1) {
rev[left] = s[right];
right--;
left++;
}
rev[left] = 0;
// Remember to use delete[]
return rev;
}
EDIT 2
Or
void Reverse(char[] s, int len) {
int left = 0; right = len;
while (right > -1) {
char t = s[left];
s[left] = s[right];
s[right] = t;
left++; right--;
}
}
Then make a copy of the string, reverse it and compare it.
Your error is the line tempstring[62] == reverse(Characters);. You don't need the double = sign. In the future, it would be helpful to post the error messages you get when compiling.
bool Palindrome(char Characters[], unsigned long length)
{
char tempstring[62];
tempstring[62] = reverse(Characters);
for(int i=0; i <= length; i++){
if(Characters[i] == tempstring[i])
return false;
else
return true;
}
}
Your error is here:
tempstring[62] == reverse(Characters);
You've wrote == means, a condition that returns true or false (for example: if (5 == 7) -> false)
But what you've actually wanted to do was tempstring[62] = reverse(Characters);
One = means equal (int a = 3)
Two == means to check a condition (for example if (a == b) (and thats why you dont write in ifs: if(a = 3) because it will assign a = 3 and allways get inside the if
First of all, your reverse function returns nothing, so attempting to assign its return value to anything is not going to work:
tempstring[62] == reverse(Characters); // won't work as it is attempting to compare a void
tempstring[62] = reverse(Characters); // won't work as it is attempting to assign a void
From a more fundamental level, testing for a palindrome is much less complex than you are making it:
bool Palindrome(char Characters[], unsigned long length)
{
bool result = true;
for(int i=0; i < length / 2; i++)
{
if (Characters[i] != Characters[length - i - 1])
{
result = false;
break;
}
}
return result;
}