Write a recursive boolean function that returns 1 if a string is a palindrome and 0 if not.
bool ispalindrome(char str[])
Notes: the function must be recursive (not a wrapper for recursive function), it has to use the given (above) signature and no global or static variables are allowed, also, we can't 'destroy' the given string.
My attempt:
bool ispalindrome(char str[]){
bool res;
if (*str == 0 || *(str + 1) == 0)
return 1;
else{
res = ispalindrome(str + 1);
Now I'm stuck, I thought of making a helper function that uses dynamic arrays that would have the first and last elements from the original string removed and use that in the recursive call but I don't think that's the intended solution.
EDIT: I've made some progress, but it doesn't work:
bool ispalindrome(char str[]){
bool res;
int l = strlen(str);
char t;
if (*str == 0 || *(str + 1) == 0)
return 1;
else{
t = str[l-1];
str[l-1] = 0;
res = ispalindrome(str + 1);
if (res == 0)
return 0;
if (str[0] == str[l - 2])
res =1;
else
res=0;
str[l] = t;
return res;
}
}
So, your current code almost works. The only issues that I see is that in one case you return before you revert the changes in the string. Also, you have an off by one error in your indexing.
Let's also note some stylistic fixes:
*(str + 1) really should be str[1]. They're equivalent, but the latter is what is expected.
Since you've already calculated l = strlen(str), you could use that for your first conditional, since it's a bit more readable what that means.
You can also use longer variable names. They aren't more expensive.
Personally, I like that if you're writing the null character into a string, that you use the null character instead of 0. That is '\0'. But that's just me.
And the code:
bool ispalindrome(char str[]){
int l = strlen(str);
// Recusive Base Case
if (l == 0 || l == 1)
return true;
// Save the last character of the string
char t = str[l-1];
str[l-1] = '\0';
// Recursive call on the substring
bool res = ispalindrome(str + 1);
// Now, this is where you messed up. Before we return,
// we need to fix the string!
str[l-1] = t;
// If the recursive call thinks that it's not a palindrome,
// then we won't disagree.
if (res == false)
return res;
// Check the (current) first position of the string against the last
// You had an off by one error here.
return (str[0] == str[l - 1]);
}
Can we make a small improvement to the runtime of the code?
One annoying property of how your code works is that we will always have around strlen(str) / 2 recursive calls. Could we make the code run faster on an example like "abracadabra", where the second letter of the string causes the palindrome test to fail.
One way to speed it up would be to do the test (str[0] == str[l - 1]) before the recursive call. We could implement this fairly easily, and it might looks something like this:
bool ispalindrome(char str[]){
int length = strlen(str);
// Recursive Base Case
if (length == 0 || length == 1)
return true;
// Check our case.
// Note that this is occuring __before__ the recursive call
if (str[0] != str[length - 1])
return false;
// Save the last character of the string
char t = str[length - 1];
str[length - 1] = '\0';
// Recursive call on the substring
bool res = ispalindrome(str + 1);
// We need to fix the string
str[length - 1] = t;
return res;
}
All of this being said...
I've seen this question a couple of times on stackoverflow, and I'm always curious what the instructor is looking for. The classic version of this problem is solved by passing the string length as an additional parameter. By doing this, we save a ton of work.
Every solution posted so far (including mine) calls strlen() on every recursive call. That means that all of these solutions are at least O(n^2). If we could pass the length to the recursive call, we could easily solve the problem in O(n). This would be an extremely large reduction in work.
Additionally, you could also then write the code in a tail recursive manner. This can allow the compiler to generate code that is much nicer for the processor to execute. (Basically, it will transform the code into what a for-loop would look like). This is very helpful because you then don't have as many concerns about running out of stack space on very large strings.
But, because of the limitations that your instructor has placed, we can't do any of these things. Which is kind of lame.
without a compiler...
bool ispalindrome(char str[])
{
int len = strlen(str);
if( len <= 1)
{
return TRUE;
}
else if (str[0] != str[len - 1])
{
reutrn FALSE;
}
else
{
char *str2 = malloc(len - 1);
strncpy(str2, str + 1, len - 2);
str2[len - 2] = NULL;
BOOL result = ispalindrome(str2);
free(str2);
return result;
}
}
psuedocode (meaning, there is no thing called string in C, and I didn't try to compile it, and my imaginary library calls that are similar to real library calls could have the params in the wrong orders, etc):
bool isPalendrome(string s)
{
if (s[0] == s[strlen(s)-1]) // if the value of the first is the same as the last
{
if ((&s[strlen(s)-1] - &s[0]) < 3)// if the address of the end and the address of the start are 1, or 2, we have a palindrome of 1 or 2 chars..
{
return true;
}
s2 = alloca(strlen(s) - 1);// or VLA , -1 because -2 char, +1 for null
s2[strlen(s) - 2] = '\0';
strncpy(s+1,s2,strlen(s)-2);
return isPalendrome(s2)
}
return false;
}
Related
Why is the following code printing !notreblo! instead of !notrebloH? Where is the ! coming from? I am trying to write a program that reverses an array and I am using the main function to test the rev_string function.
#include <stdio.h>
int main(void)
{
char s[11] = "Holberton!";
printf("%s\n", s);
rev_string(s);
printf("%s\n", s);
return (0);
}
void rev_string(char *s)
{
char new[500];
int count, newcount;
count = 0, newcount = 0;
while (*(s + count) != '\0')
{
*(new + count) = *(s + count);
count++;
}
count--;
while (count > 0)
{
*(s + newcount) = *(new + count);
count--;
newcount++;
}
}
The second while does not copy the first character, because the last character copied is at index 1. The condition tells it so: count > 0.
Change it to count >= 0.
(+1 for the famous "one-off" error. If I got 1 cent each time, I'll be a rich person.)
Notice your second while condition: while (count > 0). You aren't including your last character - e.g. if count == 3 (3 characters to reverse), you will only iterate twice - and your third character will not be written. You need to change the condition to while (count >= 0).
As a bonus, the function you are implementing is better known as strrev - and it can be implemented without an additional buffer.
Because you should change your second while condition to while (count >= 0)
I came up with the following basic item to reverse a string in C:
void reverse(char in[], char out[]) {
int string_length = 0;
for(int i=0; in[i] != '\0'; i++) {
string_length += 1;
}
for(int i=0; i < string_length ; i++) {
out[string_length-i] = in[i];
}
out[string_length+1] = '\0';
}
Is there a way to do this in one for loop or is it necessary to first use a for length to get the string length, and then do a second one to reverse it? Are there other approaches to doing a reverse, or is this the basic one?
Assuming you can't use functions to get the string length and you want to preserve the second loop I'm afraid this is the shortest way.
Just as a side-note though: this code is not very safe as at for(int i=0; in[i] != '\0'; i++) you are not considering cases where the argument passed to parameter in is not a valid C string where there isn't a single \0 in all elements of the array pointed by in and this code will end up manifesting a buffer over-read at the first for loop when it will read beyond in boundaries and a buffer overflow in the second for loop where you can write beyond the boundaries of out. In functions like this you should ask the caller for the length of both arrays in and out and use that as a max index when accessing them both.
As pointed by Rishikesh Raje in comments: you should also change the exit condition in the second for loop from i <= string_length to i < string_length as it will generate another buffer over-read when i == string_length as it will access out by a negative index.
void reverse(char *in, char *out) {
static int index;
index = 0;
if (in == NULL || in[0] == '\0')
{
out[0] = '\0';
return;
}
else
{
reverse(in + 1, out);
out[index + 1] = '\0';
out[index++] = in[0];
}
}
With no loops.
This code is surely not efficient and robust and also won't work for multithreaded programs. Also the OP just asked for an alternative method and the stress was on methods with lesser loops.
Are there other approaches to doing a reverse, or is this the basic one
Also, there was no real need of using static int. This would cause it not to work with multithreaded programs. To get it working correct in those cases:
int reverse(char *in, char *out) {
int index;
if (in == NULL || in[0] == '\0')
{
out[0] = '\0';
return 0;
}
else
{
index = reverse(in + 1, out);
out[index + 1] = '\0';
out[index++] = in[0];
return index;
}
}
You can always tweak two loops into one, more confusing version, by using some kind of condition to determine which phase in the algorithm you are in. Below code is untested, so most likely contains bugs, but you should get the idea...
void reverse(const char *in, char *out) {
if (*in == '\0') {
// handle special case
*out = *in;
return;
}
char *out_begin = out;
char *out_end;
do {
if (out == out_begin) {
// we are still looking for where to start copying from
if (*in != '\0') {
// end of input not reached, just go forward
++in;
++out_end;
continue;
}
// else reached end of input, put terminating NUL to out
*out_end = '\0';
}
// if below line seems confusing, write it out as 3 separate statements.
*(out++) = *(--in);
} while (out != out_end); // end loop when out reaches out_end (which has NUL already)
}
However, this is exactly as many loop iterations so it is not any faster, and it is much less clear code, so don't do this in real code...
I want to write a recursive function which calculates Succ(‘2468’) = '2469'. ‘2468’ is a numeric string.
The exercice give me some predefined function such as last(ch), which returns the last caracter of a string, start(ch), returns ch without the last caracter, addEnd(ch, c), adds c at the end of ch and ask me to return a string as final result (i.e., suc("123")="124")
I tried this code but it works only for a string with 2 characters. If the length of my string is > 2, it doesn't work:
int successor (char*ch)
{
if (strlen (ch)==1)
return (int(*ch))+1);
else
return ((int(*ch))*10+successor(ch+1));}
There seems to be no need for multiplication or use of powers. Given the extra predefined functions you indicated were provided, I think the intention here was for a recursive way to express long addition with carry. I don't know C but here's an illustration in JavaScript that has very close syntax. I hope this helps.
function successor(s){
if (s.length == 1){
if (s == '9'){
return '10';
} else {
// Return the successor character,
// not sure how this looks in C.
return String.fromCharCode(
s.charCodeAt(0) + 1);
}
}
let rightmost = successor(last(s));
// No carry so just return
// the string with the last
// character incremented
if (rightmost != '10'){
return addEnd(start(s), rightmost);
// We have a carry so
// continue the recursion
} else {
return addEnd(successor(start(s)), '0');
}
}
function last(s){
return s.substr(-1);
}
function start(s){
return s.substr(0, s.length - 1);
}
function addEnd(s, c){
return s + c;
}
console.log(successor('2999'));
A key problem is this logic:
(int(*ch))*10+successor(ch+1)
the multiplication by 10 is insufficient for larger numbers. We need to multiply by a power of 10 and we already calculated that power but didn't hang onto it:
strlen (ch)
or more specifically:
strlen(ch) - 1
A complete solution:
#include <math.h>
#include <stdio.h>
#include <string.h>
#define digit(c) (c - '0')
int successor(char *string)
{
size_t power = strlen(string) - 1;
if (power == 0)
{
return digit(*string) + 1;
}
return digit(*string) * pow(10, power) + successor(string + 1);
}
int main() {
printf("%d\n", successor("2999"));
return 0;
}
OUTPUT
> ./a.out
3000
>
TODO
What happens if successor() is passed an empty string:
printf("%d\n", successor(""));
How can you modify the code to fix this? First decide what the function should return in this situation. What happens if successor() is passed a string that represents a number too large to be contained in an int:
printf("%d\n", successor("8589934592"));
How can you modify the code to fix this? Again, first decide what the function should return in this situation.
I've tried this code but it doesn't seem to be working, how to break out of the nested loop ?
#include <stdio.h>
#include <string.h>
int meme(char s1[], char s2[])
{
int i = 0, j = 0;
int different;
while (i <= strlen(s1) && different == 1) {
while (j <= strlen(s2)) {
if (s1[i] != s2[j]) {
different = 1;
} else {
different = 0;
}
j = j + 1;
}
i = i + 1;
}
return different;
}
You have to initialize different as it is undefined if not - this probably breaks your first while loop as different probably is a random number > 1.
strlen gives you the number of characters in the string excluding the null-character which terminates the string (see here). However, you do not only compare the characters of the two strings, but also the null-character, probably to implicitely check if the length of the strings is the same. While this should work, it is better to do this check explicitely by comparing the length of the strings first as it is less error-prone.
It isn't necessary to do a nested loop here if you compare the length of the strings first. Also, you now know the length of both strings, so this function can be change to use a for loop, which makes it even simpler.
A possible solution based on the points above:
#include <stdio.h>
#include <string.h>
int meme(char s1[], char s2[]){
int i = 0;
int len_s1 = 0;
int len_s2 = 0;
int different = 0;
len_s1 = strlen(s1);
len_s2 = strlen(s2);
if (len_s1 == len_s2) {
for (i = 0 ; i < len_s1 ; i++) {
if (s1[i] != s2[i]) {
different = 1;
break;
}
}
else {
different = 1;
}
return different;
}
One more thing - do yourself and everyone else a favor and intend your code as it is extremely hard to read otherwise!
Your code is not optimized and you are not using a good approach for doing the task. I have modified the code and it will do the job with minimized complexity.
Here I assume that both the arrays are of same size as your problem shows
bool meme(char s1[], char s2[])
{
int i=0;
while(s1[i] != NULL && s2[i] != NULL)
{
if(s1[i] == s2[i])
return false;
i += 1;
}
return true;
}
When you call this function then declare a variable of type bool and store the returned value of this function in that variable.
For example :
bool check;
bool = meme(array 1 , array 2);
and then check if returned value is true, then both the arrays are totally different else not. You can do that by the below code :
if(check)
printf("Arrays are different");
else
printf("Arrays are not different");
You can also use int in place of bool if it suits you better but remember, whatever code you write, must be least complex. And think that if you are using int then also you are returning only 0 or 1; but int takes 2 bytes in 32-bit compiler and 4 bytes in 64-bit compiler, but bool takes only 1 byte and even 1 bit in some languages like pascal (if I am not wrong).
And don't get confused with return true; and return false;. True simply means 1 and false means 0. And a boolean type variable can store only binary number (1 or 0).
There is so much wrong with your code.
Why are you calling strlen() in while()? It will get executed every time till the loop doesn't exit and will cost on performance.
Also the variable different is not initialized with value 1, so how can you be so sure about the initial value of that variable?
I have tried to simplify your function still, there is scope for optimization:
int meme(char s1[], char s2[])
{
int i = 0;
int different;
int str1_len = strlen(s1);
int str2_len = strlen(s2);
if(str1_len > str2_len)
str1_len = str2_len;
do{
if(s1[i] == s2[i])
{
printf("Common\n");
different = 0;
}
else
{
different = 1;
}
i++;
}while(str1_len--);
return different;
}
I wanted to write a recursive function that gets a string pointer and returns int; it should return 1 if the string is in alphabetic order like "abbcdj", and otherwise it should return 0.
int CheckS(char *string)
{
if (string == NULL)
return 1;
if (strncmp(string, string + 1, 1) >= 0)
CheckS(string + 1);
else
return 0;
}
but it does not work like it's supposed to. My idea was to take a letter and the next and check if the next is coming previous in alphabetic order or not; also I check this
if (string-(string+1)>=0)
CheckS(string + 1);
but no result.
Your compiler should be complaining that your function does not always return a value. It doesn't protect itself from empty strings (though it does handle null pointers, which is often good), and it mishandles the comparison of the last non-null byte with the trailing null byte in the string. Also, I think your comparison is using the wrong sign. Using your technology, you could write:
int CheckS(char *string)
{
if (string == NULL || *string == '\0' || *(string + 1) == '\0')
return 1;
if (strncmp(string, string + 1, 1) <= 0)
return CheckS(string + 1);
else
return 0;
}
The strncmp() function will return a negative value if *string precedes *(string + 1); zero if the characters are equal, and a positive value if *string is greater than *(string + 1). However, it would be more direct to write:
int CheckS(char *string)
{
if (string == NULL || string[0] == '\0' || string[1] == '\0')
return 1;
if (string[0] <= string[1])
return CheckS(string + 1);
else
return 0;
}
Note that this does not reject or ignore non-alphabetic characters, nor does it case-convert them before comparison. This can be regarded as GIGO — Garbage In, Garbage Out.
Also note that I'm taking the 'recursive function' as a requirement, even though the sensible code is iterative, not recursive. The function is using a form of tail recursion, which is trivially convertible to iteration.
It would be easier if you didn't try to use strncmp but instead just compared the next two characters. You can use operator[] to access the individual characters, like so:
string[0] >= string[1]