How to remove even occurences of a substring in a string - c

i am trying to remove even occurences of a substsring, for example if i have A11B11C11 and i want to remove 11, it will remove the ones on position 0 and 2, so it should look like AB11C.
This is the function i have thus far, it removes all the occurences.
void removeSubstr(char* s, char* c)
{
int poz = -1;
int i = 0;
int string_length = strlen(s);
int number_length = strlen(c);
while (i < string_length) {
if (strstr(&s[i], c) == &s[i]) {
poz++;
if (poz % 2 == 0) {
string_length -= number_length;
for (int j = i; j < string_length; j++) {
s[j] = s[j + number_length];
}
}
}
else {
i++;
}
}
printf("'%d'", poz);
s[i] = '\0';
}
My approach was that whenever i find one occurence i should add it to a counter, and whenever the counter is divisible with 2, i remove the substring, but it always removes all the substrings.
edit:
if (strstr(&s[i], c) == &s[i]) {
poz++;
if (poz % 2 == 0) {
string_length -= number_length;
for (int j = i; j < string_length; j++) {
s[j] = s[j + number_length];
}
}
else {
i++; //added this
}
}
As was told in comment, i had to increment the i as well when there was an odd occurence, and now it works.

For starters the function should return the modified source string. Also as the substring is not changed within the function then the corresponding function parameter should be declared with the qualifier const
char * removeSubstr( char *s1, const char *s2 );
Calling the function strstr starting from each character of the source string as you are doing
while (i < string_length) {
if (strstr(&s[i], c) == &s[i]) {
is not efficient. The substring initially can be absent in the source string.
Also if you have the source string like this "A11111" then it seems the result string should look like "A111" because after the first substring "11" is removed when in this string "A111" the substring "11" is odd and must not be removed.
However using your approach you will get the result string "A1". That is in the else statement you should write
else {
i += number_length;
}
I would write the function the following way as it is shown in the demonstration program below.
#include <stdio.h>
#include <string.h>
char * removeSubstr( char *s1, const char *s2 )
{
size_t pos = 0;
size_t n1 = strlen( s1 ), n2 = strlen( s2 );
for ( char *prev = s1, *next = s1 ; ( next = strstr( prev, s2 ) ) != NULL; prev = next )
{
n1 -= next - prev - n2;
if ( pos++ % 2 == 0 )
{
memmove( next, next + n2, n1 + 1 );
}
else
{
next = prev + n2;
}
}
return s1;
}
int main( void )
{
char s1[] = "A11B11C11";
const char *s2 = "11";
printf( "\"%s\"\n", s1 );
printf( "\"%s\"\n", removeSubstr( s1, s2 ) );
}
The program output is
"A11B11C11"
"AB11C"

Related

adding zeros before string

zfill algorithm is supposed to work as follows:
zfill function accepts two parameters, a string and a number,
if string length is >= the number, then it doesn't have to add anything, and it returns a copy to the string,
else, malloc enough space and add zeros before the string.
I'm trying to understand why is this solution not correct, it has two warnings:
1st warning:
for (i; i < zeros; i++) {
s[i] = "0";
}
"=": char differs in level of indirection from char[2]
2nd warning:
for (i; i < n; i++) {
s[i] = str[i];
}
buffer overrun while writing to s
char* zfill(const char* str, size_t n) {
if (str == NULL) {
return NULL;
}
char* s;
size_t length = strlen(str);
if (length >= n) {
//it doesn't have to add anything, just malloc and copy the string
size_t sum = length + 1u;
s = malloc(sum);
if (s == NULL) {
return NULL;
}
for (size_t i = 0; i < length; i++) {
s[i] = str[i];
}
s[sum] = 0;
}
else {
// add zeros before strings
size_t zeros = n - length;
size_t sum = n + 1u;
s = malloc(sum);
if (s == NULL) {
return NULL;
}
size_t i = 0;
for (i; i < zeros; i++) {
s[i] = "0";
}
for (i; i < n; i++) {
s[i] = str[i];
}
s[sum] = 0;
}
return s;
}
int main(void) {
char str[] = "hello, world!";
size_t n = 40;
char* s = zfill(str, n);
free(s);
return 0;
}
EDIT: I've solved the problem this way:
char* zfill(const char* str, size_t n) {
if (str == NULL) {
return NULL;
}
char* s;
size_t length = strlen(str);
if (length >= n) {
//it doesn't have to add anything, just malloc and copy the string
size_t sum = length + 1u;
s = malloc(sum);
if (s == NULL) {
return NULL;
}
for (size_t i = 0; i < length; i++) {
s[i] = str[i];
}
s[sum-1] = 0;
}
else {
// add zeros before strings
size_t zeros = n - length;
size_t sum = n + 1u;
s = malloc(sum);
if (s == NULL) {
return NULL;
}
size_t i = 0;
for (i; i < zeros; i++) {
s[i] = '0';
}
for (size_t j = 0; i < n; j++) {
s[i++] = str[j];
}
s[sum-1] = 0;
}
return s;
}
and it works, but I don't know why I have this warning:
for (i; i < zeros; i++) {}
statement with no effect
but when I've debugged I've noticed that this statement has an effect, because it correctly copies the correct number of zeros. I don't know why I have this warning
SO is a place of learning.
When first dealing with a coding challenge, it's best to take time to work out what's needed before starting to write code.
Below is a working version of zfill() (along with a main() that tests it.)
Read through the comments. The only thing new here is memset().
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// A trivial "helper function" determines the max of two values
int max( int a, int b ) { return a > b ? a : b; }
char *zfill( char *str, int minLen ) {
// Determine length of arbitrary string
int len = strlen( str );
// Determine max of length v. minimum desired
int allocSize = max( minLen, len );
// allocate buffer to store resulting string (with '\0')
char *obuf = (char*)malloc( allocSize + 1 );
/* omitting test for failure */
// determine start location at which to copy str
int loc = len <= minLen ? minLen - len : 0;
if( loc > 0 )
// fill buffer with enough 'zeros'
memset( obuf, '0', allocSize ); // ASCII zero!
// copy str to that location in buffer
strcpy( obuf + loc, str );
// return buffer to calling function
return obuf;
}
int main() {
// collection of strings of arbitrary length
char *strs[] = { "abc", "abcdefghijkl", "abcde", "a", "" };
// pass each one to zfill, print, then free the alloc'd buffer.
for( int i = 0; i < sizeof strs/sizeof strs[0]; i++ ) {
char *cp = zfill( strs[i], 10 );
puts( cp );
free( cp );
}
return 0;
}
Output:
0000000abc
abcdefghijkl
00000abcde
000000000a
0000000000
Here's zfill() without the comments:
char *zfill( char *str, int minLen ) {
int len = strlen( str );
int allocSize = max( minLen, len );
char *obuf = (char*)malloc( allocSize + 1 );
/* omitting test for failure */
int loc = len <= minLen ? minLen - len : 0;
if( loc > 0 )
memset( obuf, '0', loc ); // ASCII zero!
strcpy( obuf + loc, str );
return obuf;
}
You don't want to spend your time staring at lines and lines of code.
Fill your quiver with arrows that are (proven!) standard library functions and use them.
I've omitted, too, the test for zfill being passed a NULL pointer.
This code snippet
size_t sum = length + 1u;
s = malloc(sum);
//...
s[sum] = 0;
accesses memory outside the allocated character array because the valid range of indices is [0, sum). You need to write at least like
s[length] = 0;
In this code snippet
for (i; i < zeros; ++) {
s[i] = "0";
}
the expression s[i] represents a single object of the type char while on the right-hand side there is a string literal that as an expression has the type char *. You need to write at least
s[i] = '0';
using the integer character constant instead of the string literal.
In this code snippet
size_t i = 0;
for (i; i < zeros; i++) {
s[i] = "0";
}
for (i; i < n; i++) {
s[i] = str[i];
}
as the length of the string str can be less than n then this for loop
for (i; i < n; i++) {
s[i] = str[i];
}
accesses memory outside the string str.
Pay attention to that your function has redundant code. It can be written simpler.
The function can look for example the following way as shown in the demonstration program below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * zfill( const char *s, size_t n )
{
char *result = NULL;
if ( s != NULL )
{
size_t len = strlen( s );
n = len < n ? n : len;
result = malloc( n + 1 );
if ( result )
{
size_t i = 0;
size_t m = len < n ? n - len : 0;
for ( ; i < m; i++ )
{
result[i] = '0';
}
for ( ; i < n; i++ )
{
result[i] = s[i - m];
}
result[i] = '\0';
}
}
return result;
}
int main( void )
{
const char *s = "Hello";
size_t n = 10;
char *result = zfill( s, n );
if ( result ) puts( result );
free( result );
}
The program output is
00000Hello
Or as #Some programmer dude pointed to in his comment you can use the standard C function snprintf that alone performs the task. For example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * zfill( const char *s, size_t n )
{
char *result = NULL;
if ( s != NULL )
{
size_t len = strlen( s );
n = len < n ? n : len;
result = malloc( n + 1 );
if ( result )
{
int m = len < n ? n - len : 0;
snprintf( result, n + 1, "%.*d%s", m, 0, s );
}
}
return result;
}
int main( void )
{
char *p = zfill( "Hello", 5 );
if ( p ) puts( p );
free( p );
p = zfill( "Hello", 10 );
if ( p ) puts( p );
free( p );
}
The program output is
Hello
00000Hello
so you have 3 major problems in your code :
it's s[i] = '0'; not s[i] = "0";
it's s[i] = str[i - zeros]; not s[i] = str[i]; as the value of the i will be 27 in your test case : so it make sense to say s[27] because its size is about 41 but it doesn't make sense to say str[27] as its size is only about 13 in your test case , so you had to map the value 27 of i to the value 0 to be convenient to use with str
i is deprecated in first part here for (i; i < zeros; i++) , so use for (; i < zeros; i++)instead of for (i; i < zeros; i++) , but it will not cause any problem if you keep it.
and here is the full edited code :
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char* zfill(const char* str, size_t n) {
if (str == NULL) {
return NULL;
}
char* s;
size_t length = strlen(str);
if (length >= n) {
//it doesn't have to add anything, just malloc and copy the string
size_t sum = length + 1u;
s = malloc(sum);
if (s == NULL) {
return NULL;
}
for (size_t i = 0; i < length; i++) {
s[i] = str[i];
}
s[sum] = 0;
}
else {
// add zeros before strings
size_t zeros = n - length;
size_t sum = n + 1u;
s = malloc(sum);
if (s == NULL) {
return NULL;
}
size_t i = 0;
for (; i < zeros; i++) {
s[i] = '0';
}
for (; i < n; i++) {
s[i] = str[i - zeros];
}
s[sum] = 0;
}
return s;
}
int main(void) {
char str[] = "hello, world!";
size_t n = 40;
char* s = zfill(str, n);
printf("%s\n", s);
free(s);
return 0;
}

Stuck Finding Bug in LeetCode Question Solution

The problem is simple: take a string, and reverse the position of ONLY letters (lower or uppercase). Leave any special characters where they are. My solution:
char * reverseOnlyLetters(char * S){
int Len = strlen(S);
char *StrBeg, *StrEnd, tempCh;
bool FoundStart = 0, FoundEnd = 0;
StrBeg = S;
StrEnd = S + (Len - 1);
for (int i = 0; i < (Len/2); i++)
{
if (((*StrBeg>= 'A') && (*StrBeg <= 'Z')) || ((*StrBeg >= 'a') && (*StrBeg <= 'z')))
{
FoundStart = 1;
}
else
{
StrBeg++;
}
if (((*StrEnd >= 'A') && (*StrEnd <= 'Z')) || ((*StrEnd >= 'a') && (*StrEnd <= 'z')))
{
FoundEnd = 1;
}
else
{
StrEnd--;
}
if(FoundStart && FoundEnd)
{
tempCh = *StrEnd;
*StrEnd = *StrBeg;
*StrBeg = tempCh;
StrBeg++;
StrEnd--;
FoundStart = 0;
FoundEnd = 0;
}
}
return S;
}
The issue is a testcase like "a-bC-dEf-ghIj" fails; the "E" and the "f" in the middle either don't get swapped at all, or (as I suspect), get swapped, but then get swapped BACK. Anyone see what I'm doing wrong?
Problem is this for (int i = 0; i < (Len/2); i++).
If lenght of string is even, it's ok, but in case it's even it doesn't go through the middle character. E in this "a-bC-dEf-ghIj" case, so it can't switch it with f.
The approach using this for loop
for (int i = 0; i < (Len/2); i++)
is incorrect. Let's assume that the string is "#AB". The result string will look like "#BA"
But using your loop you will have (as Len / 2 is equal to 1)
for (int i = 0; i < 1; i++)
In the first and single iteration of the loop the pointer StrBeg will be incremented
StrBeg++;
because the pointed character is not a letter.
So nothing will be reversed.
The function can be written simpler the following way
#include <stdio.h>
#include <string.h>
#include <ctype.h>
char * reverse_letters( char *s )
{
for ( char *first = s, *last = s + strlen( s ); first < last; ++first )
{
while ( first != last && !isalpha( ( unsigned char )*first ) ) ++first;
while ( last != first && !isalpha( ( unsigned char )*--last ) );
if ( first != last )
{
char c = *first;
*first = *last;
*last = c;
}
}
return s;
}
int main( void )
{
char s[] = "one, two, three";
puts( s );
puts( reverse_letters( s ) );
char s1[] = "#AB";
puts( s1 );
puts( reverse_letters( s1 ) );
}
The program output is
one, two, three
eer, hto, wteno
#AB
#BA

Insert a string to another string in C

I need to insert a character string to another character string.
I'd write my own code and it's work very well until I put SPACE character in insert character string.
I need to find out what is the problem of my code and How I can fix it.
//Insert string in another string
#include <stdio.h>
//Function to count number of characters in a string
int numOfChars(const char string[]);
//Function to insert string to another string
void insertString(char string[], char insert[], int position);
int main()
{
char str[20]= "145";
insertString(str, "23", 1);
printf("%s\n", str);
return 0;
}
int numOfChars(const char string[])
{
int counter = 0;
while(string[counter] != '\0')
counter++;
return counter;
}
void insertString(char string[], char insert[], int position)
{
int i, j;
int lenght1 = numOfChars(string);
int lenght2 = numOfChars(insert);
int finalLenght = lenght1 + lenght2;
for(i=lenght1, j=0; i<finalLenght; i++, position++, j++)
{
string[i] = string[position];
string[position] = insert[j];
}
string[finalLenght] = '\0';
}
Example 1 :
Main string : 145
Insert string : 23
Position : 2
Result : 12345
Example 2 with blank space :
Main string : 145
Insert string : 23[SPACE]
Position : 2
Result : 123 54
This loop
for(i=lenght1, j=0; i<finalLenght; i++, position++, j++)
{
string[i] = string[position];
string[position] = insert[j];
}
is incorrect because independent on the given position all characters from the source string stating at position position are written after the end of the source string.
Also the function insertString should be declared like
char * insertString(char string[], const char insert[], size_t position);
Here is a demonstrative program that shows how the function can be implemented.
#include <stdio.h>
size_t numOfChars( const char s[] )
{
size_t n = 0;
while ( s[n] != '\0' ) ++n;
return n;
}
char * insertString( char s1[], const char s2[], size_t pos )
{
size_t n1 = numOfChars( s1 );
size_t n2 = numOfChars( s2 );
if ( n1 < pos ) pos = n1;
for ( size_t i = 0; i < n1 - pos; i++ )
{
s1[n1 + n2 - i - 1] = s1[n1 - i - 1];
}
for ( size_t i = 0; i < n2; i++)
{
s1[pos+i] = s2[i];
}
s1[n1 + n2] = '\0';
return s1;
}
int main(void)
{
enum { N = 20 };
char s1[N] = "123";
puts( insertString( s1, "AB", 0 ) );
char s2[N] = "123";
puts( insertString( s2, "AB", 1 ) );
char s3[N] = "123";
puts( insertString( s3, "AB", 2 ) );
char s4[N] = "123";
puts( insertString( s4, "AB", 3 ) );
return 0;
}
The program output is
AB123
1AB23
12AB3
123AB
Changing string[i] = string[position]; to string[position+lenght2] = string[position]; should solve the problem.
While copying over the elements of string[i] to their proper places, the length of the string to insert should be considered in addition to the position variable. In other words, you should shift the characters in string by the number of elements that need to be inserted in the middle.

how to compare two 2 d string without using strcmp

I have data file in which some data is kept.
example: welcome user HII if while
I have made 2D character array to store all the keywords in c.
now I want two know if the data file contain the keyword or not.
enter code here
for(i=0;i<32;i++)
for(j=0;j<no_of_words_in_file;j++)
if(k[i]==t[j])
printf("%s is keyword",t[j]);
here the k[i] represents the 2D character array where all the keywords in c are stored and t[i] represents the 2D character array where all the words of file are stored.
I want to compare these 2D arrays without using strcmp.
To compare two strings without using standard C functions you can use a loop like that
#include <stdio.h>
int main(void)
{
char key[] = "while";
char word1[] = "while";
char word2[] = "when";
size_t i = 0;
while ( key[i] != '\0' && key[i] == word1[i] ) ++i;
int equal = key[i] == word1[i];
printf( "key == word1: = %d\n", equal );
i = 0;
while ( key[i] != '\0' && key[i] == word2[i] ) ++i;
equal = key[i] == word2[i];
printf( "key == word2: = %d\n", equal );
return 0;
}
The program output is
key == word1: = 1
key == word2: = 0
Or you can write a separate function. For example
#include <stdio.h>
int equal( const char *s1, const char *s2 )
{
while ( *s1 != '\0' && *s1 == *s2 )
{
++s1; ++s2;
}
return *s1 == *s2;
}
int main(void)
{
enum { N = 10 };
char key[][N] ={ "if", "while" };
const size_t N1 = sizeof( key ) / sizeof( *key );
char words[][N] = { "welcome", "user", "HII", "if", "while" };
const size_t N2 = sizeof( words ) / sizeof( *words );
for ( size_t i = 0; i < N2; i++ )
{
for ( size_t j = 0; j < N1; j++ )
{
if ( equal( key[j], words[i] ) )
{
printf( "\"%s\" == \"%s\"[%zu]\n", key[j], words[i], i );
}
}
}
return 0;
}
the program output is
"if" == "if"[3]
"while" == "while"[4]

Copying strings between vectors of strings in C

I have an array of char pointers (string array), which contains some duplicate values. I've found an algorithm that truncates the array by removing its duplicate values.
Here is a code sample :
int i, j , k;
int n = 10;
char *teams[n];
for(i=0;i<n;i++){
for(j=i+1;j<n;){
if(*(team[j]==*(team[i])){
for(k=j;k<n;k++){
//strcpy(team[k], team[k+1]);
team[k] = team[k+1];
}
n--;
}else{
j++;
}
}
}
I've read that the only way to copy strings between string arrays is to use strcpy(s1, s2). But in my case I can't use it, because strcpy function permits to copy s2 into s1 only if s2 has a lenght equal or bigger than the lenght of s1. So how can I implement this algorithm if I can't put the string pointed by the pointer team[k+1] in team[k] ?
It seems you need to remove duplicated string representations instead of duplicated addresses to strings.
If so then this if statement (if to add missed closed parenthesis)
if( *(team[j] ) ==*( team[i] ) ){
compares only first characters of strings instead of comparing strings pointed to by the pointers.
In this loop
for(k=j;k<n;k++){
//strcpy(team[k], team[k+1]);
team[k] = team[k+1];
}
each time when a duplicates string is found there is copied the whole array of pointers. Moreover there is an attempt to access memory beyond the array in this statement when k is equal to n-1
team[k] = team[k+1];
^^^^
You can write a separate function that will "remove" duplicates. The function can for example return pointer after the last unique element in the modified array.
#include <stdio.h>
#include <string.h>
char ** unique( char *s[], size_t n )
{
size_t i = 0;
for ( size_t j = 0; j < n; j++ )
{
size_t k = 0;
while ( k < i && strcmp( s[k], s[j] ) != 0 ) ++k;
if ( k == i )
{
if ( i != j ) s[i] = s[j];
++i;
}
}
return s + i;
}
int main(void)
{
char * s[] = { "A", "B", "A", "C", "A" };
const size_t N = sizeof( s ) / sizeof( *s );
for ( size_t i = 0; i < N; i++ ) printf( "%s ", s[i] );
printf( "\n" );
char **p = unique( s, N );
size_t n = p - s;
for ( size_t i = 0; i < n; i++ ) printf( "%s ", s[i] );
printf( "\n" );
return 0;
}
The program output is
A B A C A
A B C
#include <stdio.h>
#include <string.h>
unsigned dedup(char **arr, unsigned count)
{
unsigned this, that ;
for(this=0;this<count;this++){
for(that=this+1;that<count;){
if( strcmp(arr[that], arr[this])) {that++; continue; }
#if PRESERVE_ORDER
memmove(arr+that, arr+that+1, (--count - that) * sizeof arr[that] );
#else
arr[that] = arr[--count];
#endif
}
}
return count; /* the count after deduplication */
}
char *array[] = { "one", "two", "three", "two", "one", "four", "five", "two" };
int main(void)
{
unsigned count, index;
count = dedup(array, 8);
for (index = 0; index < count; index++) {
printf("%s\n", array[index] );
}
return 0;
}
[UPDATED]: I added the PRESERVE_ORDER version

Resources