Related
I need to make a program that will print characters in a word on how frequent it is used. The unique characters will be printed in increasing order (spaces are ignored), if there are ties the character with lower ascii value will be printed first.
For an example if the input is hello world, the letters "h", "e", "w", "r" and "d" are only used once, the character "o" is used twice and the character "l" is used thrice. Since h,e,w,r,d are tie we should sort it into d,e,h,r,w. Then next would be o since it is used twice and then last is l. Thus if the input is hello world the output must be dehrwol. On my current program the problem is that when there are ties, it would not sort it alphabetically so the output is hewrdol instead of dehrwol.
This is the code I have written
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int times[256];
int cmpLetters(const void* a, const void* b)
{
return (times[*(char*)a] > times[*(char*)b]) - (times[*(char*)a] < times[*(char*)b]);
}
int main()
{
char letters[256];
int i, j, k, lnum, t;
char s[1000];
fgets(s, sizeof(s), stdin);
// Init occurrences as 0
memset(times, 0, sizeof(times));
for (i = lnum = 0; s[i] != '\0'; i++)
if (times[s[i]]++ == 0)
letters[lnum++] = s[i];
// Sort letters by number of occurrences
qsort(letters, lnum, sizeof(char), cmpLetters);
char* new = malloc(sizeof(char) * (i + 1));
for (j = k = 0; j < lnum; j++)
for (i = 0; i < times[letters[j]]; i++)
new[k++] = letters[j];
// new[k] = '\0';
for (i = 0; i<lnum; i++)
{
if(letters[i] != '\n' && letters[i] !=' ')
printf("%c",letters[i]);
}
printf("\n\n");
return 0;
}
In this for loop
for (i = lnum = 0; s[i] != '\0'; i++)
if (times[s[i]]++ == 0)
letters[lnum++] = s[i];
you are not checking whether s[i] represents a letter.
The comparison function
int cmpLetters(const void* a, const void* b)
{
return (times[*(char*)a] > times[*(char*)b]) - (times[*(char*)a] < times[*(char*)b]);
}
compares only characters without comparing also their frequencies.
This code snippet
char* new = malloc(sizeof(char) * (i + 1));
for (j = k = 0; j < lnum; j++)
for (i = 0; i < times[letters[j]]; i++)
new[k++] = letters[j];
does not make sense because the array new is not used further in the program. It only produces a memory leak.
The program will be simpler if to introduce a structure that contains two data members that store a letter and its frequency.
Here is a demonstration program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
struct Pair
{
char c;
size_t n;
};
int cmp( const void *a, const void *b )
{
const struct Pair *p1 = a;
const struct Pair *p2 = b;
int result = ( p2->n < p1->n ) - ( p1->n < p2->n );
if (result == 0)
{
result = ( p2->c < p1->c ) - ( p1->c < p2->c );
}
return result;
}
int main( void )
{
enum { N = 1000} ;
char s[N];
fgets( s, sizeof( s ), stdin );
size_t n = 0;
for (size_t i = 0; s[i] != '\0'; ++i)
{
if (isalpha( ( unsigned char )s[i] ))
{
size_t j = 0;
while (j != i && s[j] != s[i]) ++j;
n += j == i;
}
}
if (n != 0)
{
struct Pair pairs[n];
memset( pairs, 0, n * sizeof( struct Pair ) );
for (size_t i = 0, m = 0; s[i] != '\0'; i++)
{
if (isalpha( ( unsigned char )s[i] ))
{
size_t j = 0;
while (j != m && pairs[j].c != s[i]) ++j;
if (j == m)
{
pairs[m].c = s[i];
++pairs[m].n;
++m;
}
else
{
++pairs[j].n;
}
}
}
qsort( pairs, n, sizeof( *pairs ), cmp );
for (size_t i = 0; i < n; i++)
{
putchar( pairs[i].c );
}
putchar( '\n' );
}
}
The program output might look like
hello world
dehrwol
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;
}
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"
The last part of the code where I am allocating memory to common_set is not working whereas other mallocs worked fine above. Similarly when I freed the unique_set for the third time it is not done properly. I am not able to get the printf sentences which I wrote after them. I am not able to free the pointer of the union_set. Also I am not able to allocate memory to a new pointer common_pointer.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
void dict(char **str, int man, int MAY) {
char temp[MAY];
for (int i = 0; i < man; ++i) {
for (int j = i + 1; j < man; ++j) {
if (strcmp(str[i], str[j]) > 0) {
strcpy(temp, str[i]);
strcpy(str[i], str[j]);
strcpy(str[j], temp);
}
}
}
}
//int k = 0;
char **unique_set;
char **a_new_sumset;
int unique(char **s, int l, int d, int k) {
if (d == l) {
unique_set[k] = (char*)malloc((strlen(s[l]) + 1) * sizeof(char));
strcpy(unique_set[k], s[l]);
return k;
} else {
for (int i = l; i < d; i++) {
if (strcmp(s[i], s[i + 1]) == 0 && i + 1 == d) {
unique_set[k] = (char*)malloc((strlen(s[i + 1]) + 1) * sizeof(char));
strcpy(unique_set[k], s[i + 1]);
return k;
//printf("demo: %s\n", unique_set[k]);
} else
if (strcmp(s[i], s[i + 1]) != 0) {
unique_set[k] = (char*)malloc((strlen(s[i]) + 1) * sizeof(char));
strcpy(unique_set[k], s[i]);
//printf("demo1: %s\n", unique_set[k]);
k++;
unique(s, i + 1, d, k);
break;
}
}
}
}
char **common_set;
char **intersection(char **sum_set, int d) {
int k = 0;
for (int i = 0; i < d; i++) {
if (strcmp(sum_set[i], sum_set[i + 1]) == 0 && strcmp(sum_set[i], sum_set[i + 2]) != 0 && i + 2 < d) {
common_set[k] = (char*)malloc((strlen(sum_set[i]) + 1) * sizeof(char));
strcpy(common_set[k], sum_set[i]);
k++;
} else
if (strcmp(sum_set[i], sum_set[i + 1]) == 0 && i + 2 > d) {
common_set[k] = (char*)malloc((strlen(sum_set[i]) + 1) * sizeof(char));
strcpy(common_set[k], sum_set[i]);
k++;
}
}
return common_set;
}
#define maxx1 3 // number of words in grp 1
#define maxy1 10 // word limit for the first group of words
#define maxx2 3 // number of words in grp2
#define maxy2 10 // word limit for the next group of words
int main() {
char **sum_set;
char **str_g1;
char **str_g2;
char words1[maxy1];
str_g1 = (char**)malloc(maxx1 * sizeof(char*));
char words2[maxy2];
str_g2 = (char**)malloc(maxx2 * sizeof(char*));
printf("Enter the first group:\n");
for (int i = 0; i < maxx1; i++) {
gets(words1);
str_g1[i] = (char*)malloc((strlen(words1) + 1) * sizeof(char));
strcpy(str_g1[i], words1);
//puts(ptr[i]);
//printf("%d", i);
}
printf("Enter the second group:\n");
for (int i = 0; i < maxx2; i++) {
gets(words2);
str_g2[i] = (char*)malloc((strlen(words2) + 1) * sizeof(char));
strcpy(str_g2[i], words2);
//puts(ptr[i]);
//printf("%d", i);
}
dict(str_g1, maxx1, maxy1); //to lexicographically arrange the string 1 group
dict(str_g2, maxx2, maxy2);
sum_set = (char**)malloc((maxx1 + maxx2) * sizeof(char*));
a_new_sumset = (char**)malloc((maxx1 + maxx2) * sizeof(char*));
for (int i = 0; i < maxx1; i++) {
sum_set[i] = (char*)malloc((strlen(str_g1[i]) + 1) * sizeof(char));
strcpy(sum_set[i], str_g1[i]);
//puts(sum_set[i]);
}
for (int i = 0; i < maxx2; i++) {
sum_set[i + maxx1] = (char*)malloc((strlen(str_g2[i]) + 1) * sizeof(char));
strcpy(sum_set[i + maxx2], str_g2[i]);
//puts(sum_set[i + maxx1]);
}
dict(sum_set, maxx1 + maxx2, maxy1 + maxy2);
unique_set = (char**)malloc((maxx1) * sizeof(char*));//allocating memory to next string group to compute its set
int k = unique(str_g1, 0, maxx1 - 1, 0);
printf("%d \n", k);
printf("The set of the string A in arranged order is:\n");
for (int i = 0; i <= k; i++) {
a_new_sumset[i] = (char*)malloc((strlen(unique_set[i]) + 1) * sizeof(char));
strcpy(a_new_sumset[i], unique_set[i]);
puts(unique_set[i]);
//
}
//printf("freed the pointers\n");
for (int i = 0; i <= k; ++i) {//freeing the arrays
free(unique_set[i]);
}
free(unique_set);
//printf("freed the pointers\n");//freeing the top pointer
int a = k;
unique_set = (char**)malloc((maxx2) * sizeof(char*));//allocating memory to next string group to compute its set
k = unique(str_g2, 0, maxx2 - 1, 0);
int b = k;
printf("The set of the string B in arranged order is:\n");
for (int i = 0; i <= k; i++) {
a_new_sumset[i + 1 + a] = (char*)malloc((strlen(unique_set[i]) + 1) * sizeof(char));
strcpy(a_new_sumset[i + a + 1], unique_set[i]);
puts(unique_set[i]);
//strcpy(a_new_sumset[i + a + 1], unique_set[i]);
}
printf("%d \n", k);
for (int i = 0; i <= k; ++i) {//freeing the arrays
free(unique_set[i]);
}
free(unique_set);//freeing the top pointer
printf("freed the pointers\n");
unique_set = (char**)malloc((a + b) * sizeof(char*));//allocating memory to unique_set for computing the union of the sets
k = unique(sum_set, 0, (maxx1 + maxx2) - 1, 0);
printf("The set of the string A+B in arranged order is:\n");
for (int i = 0; i <= k; i++)
puts(unique_set[i]);
for (int i = 0; i <= k; ++i) {//freeing the arrays
free(unique_set[i]);
}
printf("freed the pointers\n");
free(unique_set);
printf("freed the pointers\n");
printf("The intersection_set of the string A+B in arranged order is:\n");
common_set = (char**)malloc((maxx1 + maxx2) * sizeof(char*));
printf("The intersection_set of the string A+B in arranged order is:\n");
char **p;
p = intersection(a_new_sumset, (maxx1 + maxx2) - 1);
printf("The intersection_set of the string A+B in arranged order is:\n");
for (int i = 0; i <maxx1 + maxx2; i++) {
puts(p[i]);
}
}
There are many issues in the code:
in function dict, you should not copy the strings, but swap the pointers instead, which is simpler and works for any string length. Furthermore the name dict is confusing for this function, prefer a more informative name such as sort_strings:
void sort_strings(char **str, int man) {
for (int i = 0; i < man; ++i) {
for (int j = i + 1; j < man; ++j) {
if (strcmp(str[i], str[j]) > 0) {
char *temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
}
}
global variables are not necessary for this task, prefer local variables, pass destination arrays as arguments and return the number of strings as the return value.
the string allocation code is duplicated everywhere... using the strdup() function or a wrapper that tests for memory allocation failure would improve readability.
avoid variable name l, which looks confusingly similar to 1
the uniq function does not return a value in the else branch, it is unclear what it does and why it is recursive.
the loop to add the strings from the second group has a bug: strcpy(sum_set[i + maxx2], str_g2[i]); should be strcpy(sum_set[i + maxx1], str_g2[i]);. This mistake has no effect now because maxx1 == maxx2 but should be fixed.
in function intersection, you compare strings at offset d + 1 and d + 2 before testing if these index values are within the array boundaries.
selecting duplicated strings in intersection makes the assumption that strings are not duplicated in group 1 and 2.
the number of common strings is not returned, nor stored into a global variable, so main() cannot determine this number and uses maxx1 + maxx2 which is always wrong for non trivial cases.
Here is a modified version using functions to read lines, sort an array, find unique values, compute the union and intersection of sets and free the string arrays:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// allocate a copy of a string or die
char *xstrdup(const char *str) {
char *p = strdup(str);
if (p == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
return p;
}
// read a line of at most max bytes
char *get_line(int max) {
char buf[max + 1];
int i = 0;
int c;
while ((c = getchar()) != EOF && c != '\n') {
if (i < max) {
buf[i++] = (char)c;
}
}
buf[i] = '\0';
return xstrdup(buf);
}
// free the strings in an array of size n
void free_strings(char **strings, int n) {
for (int i = 0; i < n; i++) {
free(strings[i]);
}
}
// copy strings in lexicographical order
int sort_strings(char **sorted, char **str, int n) {
int i, j;
for (i = 0; i < n; i++) {
for (j = i; j > 0 && strcmp(sorted[j - 1], str[i]) > 0; j--) {
sorted[j] = sorted[j - 1];
}
sorted[j] = xstrdup(str[i]);
}
return n;
}
// copy unique strings in lexicographical order
int uniq_strings(char **unique_strings, char **strings, int n) {
int i, k = 0;
for (i = 0; i < n; i++) {
if (i == 0 || strcmp(strings[i - 1], strings[i]) != 0) {
unique_strings[k++] = xstrdup(strings[i]);
}
}
return k;
}
int intersection_strings(char **intersection_set, char **str1, int n1, char **str2, int n2) {
int i = 0, j = 0, k = 0;
while (i < n1 && j < n2) {
int c = strcmp(str1[i], str2[j]);
if (c < 0) {
i++;
} else
if (c > 0) {
j++;
} else {
intersection_set[k++] = xstrdup(str1[i]);
i++;
j++;
}
}
return k;
}
int union_strings(char **union_set, char **str1, int n1, char **str2, int n2) {
int i = 0, j = 0, k = 0;
while (i < n1 && j < n2) {
int c = strcmp(str1[i], str2[j]);
if (c < 0) {
union_set[k++] = xstrdup(str1[i]);
i++;
} else
if (c > 0) {
union_set[k++] = xstrdup(str2[j]);
j++;
} else {
union_set[k++] = xstrdup(str1[i]);
i++;
j++;
}
}
while (i < n1) {
union_set[k++] = xstrdup(str1[i++]);
}
while (j < n2) {
union_set[k++] = xstrdup(str2[j++]);
}
return k;
}
void print_set(char **set, int n, const char *desc) {
if (desc) {
printf("%s", desc);
}
for (int i = 0; i < n; i++) {
puts(set[i]);
}
}
#define maxx1 3 // number of words in grp 1
#define maxy1 10 // word limit for the first group of words
#define maxx2 3 // number of words in grp2
#define maxy2 10 // word limit for the second group of words
int main() {
char *str_g1[maxx1];
char *str_s1[maxx1];
char *str_u1[maxx1];
char *str_g2[maxx2];
char *str_s2[maxx2];
char *str_u2[maxx2];
char *union_set[maxx1 + maxx2];
char *intersection_set[maxx1 < maxx2 ? maxx1 : maxx2];
printf("Enter the first group:\n");
for (int i = 0; i < maxx1; i++) {
str_g1[i] = get_line(maxy1);
}
printf("Enter the second group:\n");
for (int i = 0; i < maxx2; i++) {
str_g2[i] = get_line(maxy2);
}
sort_strings(str_s1, str_g1, maxx1);
int n1 = uniq_strings(str_u1, str_s1, maxx1);
sort_strings(str_s2, str_g2, maxx1);
int n2 = uniq_strings(str_u2, str_s2, maxx2);
print_set(str_u1, n1, "The set of the string A in arranged order is:\n");
print_set(str_u2, n2, "The set of the string B in arranged order is:\n");
// compute union and intersection
int nu = union_strings(union_set, str_u1, n1, str_u2, n2);
int ni = intersection_strings(intersection_set, str_u1, n1, str_u2, n2);
print_set(union_set, nu, "The set of the string A+B in arranged order is:\n");
print_set(intersection_set, ni, "The intersection_set of the string A*B in arranged order is:\n");
free_strings(str_g1, maxx1);
free_strings(str_g2, maxx2);
free_strings(str_s1, maxx1);
free_strings(str_s2, maxx2);
free_strings(str_u1, n1);
free_strings(str_u2, n2);
free_strings(union_set, nu);
free_strings(intersection_set, ni);
return 0;
}
To improve on this code, you can define a structure string_set to combine the string array along with variables describing its allocated size and active count. Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct string_set {
int count, size;
char **array;
} string_set;
// allocate a copy of a string or die
char *xstrdup(const char *str) {
char *p = strdup(str);
if (p == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
return p;
}
void *xalloc(size_t n) {
void *p = malloc(n);
if (p == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
return p;
}
string_set *string_set_allocate(int size) {
string_set *set = xalloc(sizeof(*set));
set->count = 0;
set->size = size;
set->array = xalloc(sizeof(*set->array) * size);
return set;
}
void string_set_free(string_set *set) {
while (set->count --> 0) {
free(set->array[set->count]);
}
free(set->array);
free(set);
}
// read a line of at most max bytes
char *get_line(int max) {
char buf[max + 1];
int i = 0;
int c;
while ((c = getchar()) != EOF && c != '\n') {
if (i < max) {
buf[i++] = (char)c;
}
}
buf[i] = '\0';
return xstrdup(buf);
}
string_set *string_set_clone(string_set *set) {
string_set *copy = string_set_allocate(set->size);
for (int i = 0; i < set->count; i++) {
copy->array[i] = xstrdup(set->array[i]);
}
copy->count = set->count;
return copy;
}
void string_set_sort(string_set *set) {
for (int i = 0; i < set->count; i++) {
for (int j = i + 1; j < set->count; j++) {
if (strcmp(set->array[i], set->array[j]) > 0) {
char *temp = set->array[i];
set->array[i] = set->array[j];
set->array[j] = temp;
}
}
}
}
void string_set_uniq(string_set *set) {
if (set->count > 0) {
int j = 1;
for (int i = 1; i < set->count; i++) {
if (strcmp(set->array[i], set->array[j - 1]) == 0) {
free(set->array[i]);
set->array[i] = NULL;
} else {
set->array[j++] = set->array[i];
}
}
set->count = j;
}
}
string_set *string_set_intersection(string_set *set1, string_set *set2) {
int n1 = set1->count;
int n2 = set2->count;
string_set *res = string_set_allocate(n1 < n2 ? n1 : n2);
int i = 0, j = 0, k = 0;
while (i < n1 && j < n2) {
int c = strcmp(set1->array[i], set2->array[j]);
if (c < 0) {
i++;
} else
if (c > 0) {
j++;
} else {
res->array[k++] = xstrdup(set1->array[i]);
i++;
j++;
}
}
res->count = k;
return res;
}
string_set *string_set_union(string_set *set1, string_set *set2) {
int n1 = set1->count;
int n2 = set2->count;
string_set *res = string_set_allocate(n1 + n2);
int i = 0, j = 0, k = 0;
while (i < n1 && j < n2) {
int c = strcmp(set1->array[i], set2->array[j]);
if (c < 0) {
res->array[k++] = xstrdup(set1->array[i]);
i++;
} else
if (c > 0) {
res->array[k++] = xstrdup(set2->array[j]);
j++;
} else {
res->array[k++] = xstrdup(set1->array[i]);
i++;
j++;
}
}
while (i < n1) {
res->array[k++] = xstrdup(set1->array[i++]);
}
while (j < n2) {
res->array[k++] = xstrdup(set2->array[j++]);
}
res->count = k;
return res;
}
void string_set_print(string_set *set, const char *desc) {
if (desc) {
printf("%s", desc);
}
for (int i = 0; i < set->count; i++) {
puts(set->array[i]);
}
}
#define maxx1 3 // number of words in grp 1
#define maxy1 10 // word limit for the first group of words
#define maxx2 3 // number of words in grp2
#define maxy2 10 // word limit for the second group of words
int main() {
string_set *str_g1 = string_set_allocate(maxx1);
string_set *str_g2 = string_set_allocate(maxx2);
printf("Enter the first group:\n");
for (int i = 0; i < maxx1; i++) {
str_g1->array[str_g1->count++] = get_line(maxy1);
}
printf("Enter the second group:\n");
for (int i = 0; i < maxx2; i++) {
str_g2->array[str_g2->count++] = get_line(maxy2);
}
string_set *str_u1 = string_set_clone(str_g1);
string_set_sort(str_u1);
string_set_uniq(str_u1);
string_set *str_u2 = string_set_clone(str_g2);
string_set_sort(str_u2);
string_set_uniq(str_u2);
string_set_print(str_u1, "The set of the string A in arranged order is:\n");
string_set_print(str_u2, "The set of the string B in arranged order is:\n");
string_set *union_set = string_set_union(str_u1, str_u2);
string_set *intersection_set = string_set_intersection(str_u1, str_u2);
string_set_print(union_set, "The set of the string A+B in arranged order is:\n");
string_set_print(intersection_set, "The intersection set of the string A*B in arranged order is:\n");
string_set_free(str_g1);
string_set_free(str_g2);
string_set_free(str_u1);
string_set_free(str_u2);
string_set_free(union_set);
string_set_free(intersection_set);
return 0;
}
The last part of the code where I am allocating memory to common_set is not working ...
The problem in this part is not the allocation, but the implementation of the intersection function. You haven't provided a means for this function to return the number of elements in the intersection set, and you haven't initialized the allocated set space - you just try to access maxx1 + maxx2 set elements, most of which are undefined. Fixing this design would be a start.
I program in C. I'm supposed to create a program which identifies what is the most common character in a string and what's the second most common character.
I'm not sure why, but it's not working. the program should put into an integer the location of it. Not a pointer but if the most common is str1[i] then it will put into an integer the value of i. And so in the second most common. If it is str1[j] than it should put into an integer the value of j. then, it's supposed to replace the most commons with the second most common. The replacement function works, there's probably a problem in the loops although I can't figure out what is it.
Here's what I have (assume all of the integers and strings are declared in the beginning):
void stringReplace(char str1[], char ch1, char ch2);
int main()
{
char str1[100];
char ch1, ch2;
int i, j, p, n, len, counter1, counter2, first, second, times;
printf("Please enter the string - maximum = 100 characters:\n");
printf("User input: ");
gets(str1);
len = strlen(str1);
for(i = 0 ; i < len ; i++)
{
counter1 = 0;
for(j = 0 ; j < len ; j++)
{
if(str1[i] == str1[j])
{
counter1++;
}
if(counter1 > counter2)
{
first = i;
}
}
counter2 = counter1;
} //character which shows up most - found.
counter2 = 0;
for(p = 0 ; p < len ; p++)
{
for(n = 0 ; n < len ; n++)
{
if(str1[p] == str1[n])
{
counter1++;
}
if(counter1 < first && counter1 > counter2)
{
second = p;
}
}
counter2 = counter1;
}
ch1 = str1[first];
ch2 = str1[second];
stringReplace(str1, ch1, ch2);
puts(str1);
return 0;
}
void stringReplace(char str1[], char ch1, char ch2)
{
int i, j, len;
len = strlen(str1);
for(i = 0 ; i <= len ; i++)
{
if(str1[i] == ch1)
{
str1[i] = ch2;
}
}
}
Where's the problem ?
So you want to find the n max numbers in a string, being n=2, right?
I did a little working example for you. The code differs slightly from yours but it works well.
#include <stdio.h>
int main(int argc, char* argv[])
{
char str[] = "Algorithms Are Funnnn!\0";
int i=0;
int offset=33;
int ocurrs[94] = {0}; //considering from 33 to 126 (valid chars - ASCII Table [0-127]).
int max[2]={0};
while(str[i])
ocurrs[str[i++]-offset]++;
for(i=0; i<94; i++)
if(ocurrs[i]>ocurrs[max[1]]){
max[0] = max[1];
max[1] = i;
}
else if(ocurrs[i]>ocurrs[max[0]])
max[0]=i;
printf("chars '%c'(%d times) and '%c'(%d times) occurred most.\n",
offset+max[1], ocurrs[max[1]], offset+max[0], ocurrs[max[0]]);
return 0;
}
Also, try to stay away from gets as it's totally unsafe.
If you want to grab 100 chars max, use this instead:
char buffer[100];
fgets(buffer, 100, stdin);
Regards.
Can't resist to find an answer with some loops, without the populate occurence technique.
Very fun to code.
#include <string.h>
#include <stdio.h>
int main( void ) {
char szInput[] = "ONE DOES NOT SIMPLY WALK INTO MORDOR!";
int len = strlen( szInput );
int MaxCountSoFar_1 = 0;
int MaxIndexSoFar_1 = -1;
int MaxCountSoFar_2 = 0;
int MaxIndexSoFar_2 = -1;
int i, j, CountThatOne;
for ( i = 0; i < len; ++i ) {
if ( szInput[ i ] == ' ' ) continue;
// count that char
CountThatOne = 1;
// don't start from 0, they are already "counted"
for ( j = i + 1; j < len; ++j ) {
if ( szInput[ i ] == szInput[ j ] ) ++CountThatOne;
}
if ( CountThatOne > MaxCountSoFar_1 ) {
// push old first max to new second max
MaxCountSoFar_2 = MaxCountSoFar_1;
MaxIndexSoFar_2 = MaxIndexSoFar_1;
// new first max
MaxCountSoFar_1 = CountThatOne;
MaxIndexSoFar_1 = i;
} else {
// catch second one, but not if equal to first
if ( CountThatOne > MaxCountSoFar_2 && szInput[ i ] != szInput[ MaxIndexSoFar_1 ] ) {
MaxCountSoFar_2 = CountThatOne;
MaxIndexSoFar_2 = i;
}
}
}
if ( MaxIndexSoFar_1 >= 0 ) {
printf( "Most seen char is %c, first seen at index %d\n", szInput[ MaxIndexSoFar_1 ], MaxIndexSoFar_1 );
if ( MaxIndexSoFar_2 >= 0 ) {
printf( "Second Most seen char is %c, first seen at index %d\n", szInput[ MaxIndexSoFar_2 ], MaxIndexSoFar_2 );
}
}
return 0;
}