Effective way of checking if a given string is palindrome in C - c

I was preparing for my interview and started working from simple C programming questions. One question I came across was to check if a given string is palindrome. I wrote a a code to find if the user given string is palindrome using Pointers. I'd like to know if this is the effective way in terms of runtime or is there any enhancement I could do to it. Also It would be nice if anyone suggests how to remove other characters other than letters (like apostrophe comas) when using pointer.I've added my function below. It accepts a pointer to the string as parameter and returns integer.
int palindrome(char* string)
{
char *ptr1=string;
char *ptr2=string+strlen(string)-1;
while(ptr2>ptr1){
if(tolower(*ptr1)!=tolower(*ptr2)){
return(0);
}
ptr1++;ptr2--;
}
return(1);
}

"how to remove other characters other than letters?"
I think you don't want to actually remove it, just skip it and you could use isalpha to do so. Also note that condition ptr2 > ptr1 will work only for strings with even amount of characters such as abba, but for strings such as abcba, the condition should be ptr2 >= ptr1:
int palindrome(char* string)
{
size_t len = strlen(string);
// handle empty string and string of length 1:
if (len == 0) return 0;
if (len == 1) return 1;
char *ptr1 = string;
char *ptr2 = string + len - 1;
while(ptr2 >= ptr1) {
if (!isalpha(*ptr2)) {
ptr2--;
continue;
}
if (!isalpha(*ptr1)) {
ptr1++;
continue;
}
if( tolower(*ptr1) != tolower(*ptr2)) {
return 0;
}
ptr1++; ptr2--;
}
return 1;
}
you might need to #include <ctype.h>

How about doing like this if you want to do it using pointers only:
int main()
{
char str[100];
char *p,*t;
printf("Your string : ");
gets(str);
for(p=str ; *p!=NULL ; p++);
for(t=str, p-- ; p>=t; )
{
if(*p==*t)
{
p--;
t++;
}
else
break;
}
if(t>p)
printf("\nPalindrome");
else
printf("\nNot a palindrome");
getch();
return 0;
}

int main()
{
const char *p = "MALAYALAM";
int count = 0;
int len = strlen(p);
for(int i = 0; i < len; i++ )
{
if(p[i] == p[len - i - 1])
count++;
}
cout << "Count: " << count;
if(count == len)
cout << "Palindrome";
else
cout << "Not Palindrome";
return 0;
}

I have actually experimented quite a lot with this kind of problem.
There are two optimisations that can be done:
Check for odd string length, odd stings can't be palindromes
Start using vectorised compares, but this only really gives you performance if you expect a lot of palindromes. If the majority of your strings aren't palindromes you are still best off with byte by byte comparisons. In fact my vectorised palindrome checker ran 5% slower then the non-vectorised just because palindromes were so rare in the input. The extra branch that decided vectorised vs non vectorised made this big difference.
Here is code draft how you can do it vectorised:
int palindrome(char* string)
{
size_t length = strlen(string);
if (length >= sizeof(uintptr_t)) { // if the string fits into a vector
uintptr_t * ptr1 = (uintptr_t*)string;
size_t length_v /= sizeof(uintptr_t);
uintptr_t * ptr2 = (uintptr_t*)(string + (length - (length_v * sizeof(uintptr_t)))) + length_v - 1;
while(ptr2>ptr1){
if(*ptr1 != bswap(*ptr2)){ // byte swap for your word length, x86 has an instruction for it, needs to be defined separately
return(0);
}
ptr1++;ptr2--;
}
} else {
// standard byte by byte comparison
}
return(1);
}

Related

How to use two pointer to define a string isPalindrome?

Input: s = "A man, a plan, a canal: Panama"
Output: true
Explanation: "amanaplanacanalpanama" is a palindrome.
bool isPalindrome(char * s){
if(strlen(s) == 0) return true;
int m = 0;
for(int i = 0; i < strlen(s); i++)
if(isalnum(s[i])) s[m++] = tolower(s[i]);
int i = 0;
while(i<m)
if(s[i++] != s[--m]) return false;
return true;
}
My code's running time is 173ms. My instructor suggested me to use two pointers to improve the performance and memory usage, but I have no idea where to start.
Just position the two pointers like this
char* first = someString;
char* end = someString + strlen(s) - 1;
Now for it to be a palindrome what first and end point to must be the same
e.g. char someString[] = "1331";
So you in the first iteration *first == *last i.e. '1'
Now move the pointers towards each other until there is nothing left to compare or when they differ
++first, --end;
now *first and *last point to '3'
and so on, check if they are pointing to the same or have passed each other it is a palindrome.
Something like this
#include <stdio.h>
#include <string.h>
int palindrome(char* str)
{
char* start = str;
char* end = str + strlen(str) - 1;
for (; start < end; ++start, --end )
{
if (*start != *end)
{
return 0;
}
}
return 1;
}
int main()
{
printf("palindrome: %d\n", palindrome("1331"));
printf("palindrome: %d\n", palindrome("132331"));
printf("palindrome: %d\n", palindrome("74547"));
return 0;
}
You should add error checks, there are no error checks in the function.
My code's running time is 173ms. My instructor suggested me to use two pointers to improve the performance and memory usage, but I have no idea where to start.
It's already running in O(n) so you cannot reduce the time complexity (except for the iterative call to strlen, see below), although there are some room for improving performance.
Your function does not declare any arrays, and only use a few variables and the memory usage does not depend at all on input size. The memory usage is already O(1) and very low, so it's not a real concern.
But if you want to do it with pointers, here is one:
bool isPalindrome(char * s){
char *end = s + strlen(s);
char *a = s;
char *b = end-1;
while(true) {
// Skip characters that's not alphanumeric
while( a != end && !isalnum(*a) ) a++;
while( b != s && !isalnum(*b) ) b--;
// We're done when we have passed the middle
if(b < a) break;
// Perform the check
if(tolower(*a) != tolower(*b)) return false;
// Step to next character
a++;
b--;
}
return true;
}
When it comes to performance, your code has two issues, none of which gets solved by pointers. First one is that you're calling strlen for each iteration. The second is that you don't need to loop through the whole array, because that's checking it twice.
for(int i = 0; i < strlen(s); i++)
should be
size_t len = strlen(s);
for(size_t i = 0; i < len/2; i++)
Another remark I have on your code is that it changes the input string. That's not necessary. If I have a function that is called isPalindrome I'd expect it to ONLY check if the string is a palindrome or not. IMO, the signature should be bool isPalindrome(const char * s)

Check if Char Array contains special sequence without using string library on Unix in C

Let‘s assume we have a char array and a sequence. Next we would like to check if the char array contains the special sequence WITHOUT <string.h> LIBRARY: if yes -> return true; if no -> return false.
bool contains(char *Array, char *Sequence) {
// CONTAINS - Function
for (int i = 0; i < sizeof(Array); i++) {
for (int s = 0; s < sizeof(Sequence); s++) {
if (Array[i] == Sequence[i]) {
// How to check if Sequence is contained ?
}
}
}
return false;
}
// in Main Function
char *Arr = "ABCDEFG";
char *Seq = "AB";
bool contained = contains(Arr, Seq);
if (contained) {
printf("Contained\n");
} else {
printf("Not Contained\n");
}
Any ideas, suggestions, websites ... ?
Thanks in advance,
Regards, from ∆
The simplest way is the naive search function:
for (i = 0; i < lenS1; i++) {
for (j = 0; j < lenS2; j++) {
if (arr[i] != seq[j]) {
break; // seq is not present in arr at position i!
}
}
if (j == lenS2) {
return true;
}
}
Note that you cannot use sizeof because the value you seek is not known at run time. Sizeof will return the pointer size, so almost certainly always four or eight whatever the strings you use. You need to explicitly calculate the string lengths, which in C is done by knowing that the last character of the string is a zero:
lenS1 = 0;
while (string1[lenS1]) lenS1++;
lenS2 = 0;
while (string2[lenS2]) lenS2++;
An obvious and easy improvement is to limit i between 0 and lenS1 - lenS2, and if lenS1 < lenS2, immediately return false. Obviously if you haven't found "HELLO" in "WELCOME" by the time you've gotten to the 'L', there's no chance of five-character HELLO being ever contained in the four-character remainder COME:
if (lenS1 < lenS2) {
return false; // You will never find "PEACE" in "WAR".
}
lenS1minuslenS2 = lenS1 - lenS2;
for (i = 0; i < lenS1minuslenS2; i++)
Further improvements depend on your use case.
Looking for the same sequence among lots of arrays, looking for different sequences always in the same array, looking for lots of different sequences in lots of different arrays - all call for different optimizations.
The length and distribution of characters within both array and sequence also matter a lot, because if you know that there only are (say) three E's in a long string and you know where they are, and you need to search for HELLO, there's only three places where HELLO might fit. So you needn't scan the whole "WE WISH YOU A MERRY CHRISTMAS, WE WISH YOU A MERRY CHRISTMAS AND A HAPPY NEW YEAR" string. Actually you may notice there are no L's in the array and immediately return false.
A balanced option for an average use case (it does have pathological cases) might be supplied by the Boyer-Moore string matching algorithm (C source and explanation supplied at the link). This has a setup cost, so if you need to look for different short strings within very large texts, it is not a good choice (there is a parallel-search version which is good for some of those cases).
This is not the most efficient algorithm but I do not want to change your code too much.
size_t mystrlen(const char *str)
{
const char *end = str;
while(*end++);
return end - str - 1;
}
bool contains(char *Array, char *Sequence) {
// CONTAINS - Function
bool result = false;
size_t s, i;
size_t arrayLen = mystrlen(Array);
size_t sequenceLen = mystrlen(Sequence);
if(sequenceLen <= arrayLen)
{
for (i = 0; i < arrayLen; i++) {
for (s = 0; s < sequenceLen; s++)
{
if (Array[i + s] != Sequence[s])
{
break;
}
}
if(s == sequenceLen)
{
result = true;
break;
}
}
}
return result;
}
int main()
{
char *Arr = "ABCDEFG";
char *Seq = "AB";
bool contained = contains(Arr, Seq);
if (contained)
{
printf("Contained\n");
}
else
{
printf("Not Contained\n");
}
}
Basically this is strstr
const char* strstrn(const char* orig, const char* pat, int n)
{
const char* it = orig;
do
{
const char* tmp = it;
const char* tmp2 = pat;
if (*tmp == *tmp2) {
while (*tmp == *tmp2 && *tmp != '\0') {
tmp++;
tmp2++;
}
if (n-- == 0)
return it;
}
tmp = it;
tmp2 = pat;
} while (*it++ != '\0');
return NULL;
}
The above returns n matches of substring in a string.

Reversing a string without two loops?

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...

segmentation fault on chars array [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I need to count how many strings there is in a "string list". Each string ends as usual with a NUL char ('\0'), and the list ends with two NUL chars in succession.
I wrote a function but I keep getting a segmentation fault:
int numStrsInList(const char* strList) {
int count = 0;
int flag = 0;
if(!(*strList))
return -1;
while (flag != 2) {
if (!(*strList)) {
count++;
flag++;
}
else
flag = 0;
strList++;
}
return count;
}
for example:
const char* empty = "\0";
const char* one = "Hell0 \t\n\v\f\rw0r1d\0";
const char* two = "Hello\0 \t\0";
const char* simple = "Hello\0world\0!\0";
the invocation for example:
numStrsInList(empty)
numStrsInList(one)
numStrsInList(two)
numStrsInList(simple)
for this strings the output should be:
0
1
2
3
There are several issues with your code....
int numStrsInList(const char* strList) {
int count = 0;
int flag = 0;
if(!(*strList)) // this is not right, numStrsInList("\0") returns -1 instead of 0
return -1; // did you mean if (!strlist) ??
while (flag != 2) {
if (!(*strList)) { // maybe using this notation if (!strlist[0])
count++; // would help in avoiding the error above
flag++; // c library has strlen() functions
} // that are much faster and will make your code more readable
else
flag = 0;
strList++;
}
return count;
}
}
Compare to, overall length added per request :)
int numStrsInList(const char* strList, int maxlen)
{
// returns the number of strings in a null terminated array of
// contiguous null-terminated strings.
// maxlen is the maximum overall length of the buffer,
// can be 0 to defeat length checking
const char* s;
int result = 0;
if (!strList) return -1;
for (s = strlist;
s > (char*)1 && s[0] != 0;
s = (maxlen) ? (memchr(s, 0, maxlen - (s - strlist)) + 1)
: (s + strlen(s) + 1) )
{
if ((s - strlist) > maxlen) return -1;
++result;
}
return result;
}
Just use standard C function strchr declared in the header <string.h>.
For example
#include <stdio.h>
#include <string.h>
size_t numStrsInList(const char *s)
{
size_t n = 0;
if (!(s[0] == '\0' && s[1] == '\0'))
{
do
{
s = strchr(s, '\0');
++n;
} while (*++s);
}
return n;
}
int main( void )
{
printf("The number of substrings is %zu\n", numStrsInList("\0"));
printf("The number of substrings is %zu\n", numStrsInList("Hell0 \t\n\v\f\rw0r1d\0")) ;
printf("The number of substrings is %zu\n", numStrsInList("Hello\0 \t\0"));
printf("The number of substrings is %zu\n", numStrsInList("Hello\0world\0!\0"));
}
The program output is
The number of substrings is 0
The number of substrings is 1
The number of substrings is 2
The number of substrings is 3
Without using the standard function strchr the function can be implemented the following way
size_t numStrsInList(const char *s)
{
size_t n = 0;
if (!(s[0] == '\0' && s[1] == '\0'))
{
do
{
while (*s) ++s;
++n;
} while (*++s);
}
return n;
}
Take into account that for example this string
"\0A\0"
contains two substrings: "" and "A". While this string "\0" contains neither substring.
As for your code then already this statement
if(!(*strList))
return -1;
does not make sense.
It seems you mean
if(!strList)
return -1;
that is that the pointer strList is not equal to NULL. However by analogy with standard string functions it is better when the caller checks whether the pointer is equal to NULL.

How do I check a Palindrome in C while ignoring case sensitivity and punctuation?

I'm am currently trying to write a palindrome that ignores punctuations and case sensitivity, 1 using arrays, the 2nd using pointers. My problem is that I'm unable to figure out how. The code seems to work fine other than that. I've also written a lower case to upper case function, but I don't think it works anyhow.
This is my first code using arrays.
int is_palindrome1(const char phrase[], int length)
{
int first = phrase[0];
int last = phrase[length - 1];
for (length = 0; phrase[length] != '\0'; length++)
{
while (last > first)
{
if ((phrase[first]) != (phrase[last]))
{
return 0;
}
last--;
first++;
}
break;
}
return 1;
}
This is my 2nd palindrome code using pointers.
int is_palindrome2(const char *phrase, int length)
{
int i;
length = strlen(phrase);
for (i = 0; i < length / 2; i++)
{
if (*(phrase + i) != *(phrase + length - i - 1))
{
return 0;
}
}
return 1;
}
Here is my lower case to upper case function.
char lower_to_upper(char lower, char upper)
{
if (lower >= 'a' && lower <= 'z')
{
upper = ('A' + lower - 'a');
return upper;
}
else
{
upper = lower;
return upper;
}
}
So. Let's do this in steps.
The simplest is_palindrome function:
This will look very similar to your code. Except that some syntax problems that you have are fixed. Note that s and e point to the first and last character of the string.
bool is_palindrome(const char *phrase, unsigned length) {
const char *s = phrase + 0;
const char *e = phrase + length - 1;
while (s < e) {
if (*s != *e)
return false;
s += 1;
e -= 1;
}
return true;
}
Let's add lowercase / uppercase comparisons:
The simplest way to do this is to convert all valid characters to uppercase. It looks like you had this idea as well with your talking about a lower_to_upper() function.
The only problem is, your function has a really odd signature (why is upper an argument?). So, an easy fix to that, is to use the builtin function toupper().
bool is_palindrome(const char *phrase, unsigned length) {
const char *s = phrase + 0;
const char *e = phrase + length - 1;
while (s < e) {
if (toupper(*s) != toupper(*e))
return false;
s += 1;
e -= 1;
}
return true;
}
What about those other characters (like spaces)
Now. The last piece is that you want to ignore spaces and punctuation. Rather than wording it that way, it might be better to talk about the characters that we do want to compare. I think that you are looking to only compare alphanumeric characters. These are a-z, A-Z, and 0-9. To test if a character is one of these, we could build a custom function, or we could use the builtin isalnum() to do that:
bool is_palindrome(const char *phrase, unsigned length) {
const char *s = phrase + 0;
const char *e = phrase + length - 1;
while (s < e) {
if (!isalnum(*s)) {
s++;
} else if (!isalnum(*e)) {
e--;
} else if (toupper(*s) == toupper(*e)) {
s++;
e--;
} else {
return false;
}
}
return true;
}
Some final thoughts:
Note that on each pass of the loop, we move either s, e, or both one step. This ensures that we will eventually complete the loop. Our condition of s < e also ensure that once we reach the "middle" of the string, that we finish. I put middle in quotes, because for the string "ab a", the middle is the second character.
Languages are complicated beasts:
English has a fairly straightforward encoding in most (all?) systems. But other languages are not always that straightforward. In a comment, chux had a recommendation about this:
A locale than may have a many-to-1 mapping of lower to upper or visa-versa, using round-trip if (tolower(toupper(*s)) != tolower(toupper(*e))) handles that.
I'm personally not as concerned, because I feel that around the same point that we worry about this, we should also worry about how the text is encoded. Is it UTF-8? Is it something else? This is probably beyond your instructors expectations.

Resources