Count similar words - c

I am having an issue with this program, I want it to only displays words in the input once and count each time they appear, however it is displaying every word entered.
for example if i enter
"this should only only appear appear once"
then i want the program to output
this 1
should 1
only 2
appear 2
once 1
Any help would be appreciated.
#include <stdio.h>
#include <string.h>
#define ROW 1000
#define COL 50
int read_input(char *str, int n);
int main(void)
{
char str[ROW];
char stringSeperate[ROW][COL] = { };
const char *s= " ,.!";
char *p;
int freq[ROW];
int i = 0;
int wordCount = 0;
int pos = 0;
read_input(str, ROW);
p = strtok(str,s);
i = 1;
while(p !=NULL) {
wordCount = i;
for(i = 0; i < wordCount; i++) {
if (strcmp(p, stringSeperate[i]) != 0)
pos = 1;
else
pos = i;
}
if (pos == 1) {
strcpy(stringSeperate[i], p);
freq[i++]++;
}
else
freq[pos]++;
p = strtok(NULL,s);
}
for ( i = 1; i <= wordCount; i++ ) {
printf("Word: %s\t Number: %d\n",stringSeperate[i], freq[i]);
}
return 0;
}
int read_input(char *str, int n)
{
int ch, i = 0;
while((ch = getchar()) != '\n') {
if ( i < n ) {
*str++ = ch;
i++;
}
}
*str = '\0';
return i;
}

You invoked undefined behavior by using value of uninitialized variable freq having automatic storage duration, which is indeterminate.
Initialize it like int freq[ROW] = {0};
Also you should
1. Change initialization of stringSeperate to standard: empty initlalizer is not allowed, so it should be like
char stringSeperate[ROW][COL] = {{0}};
2. Remove extra printing to match the desired output: change
printf("Word: %s\t Number: %d\n",stringSeperate[i], freq[i]);
to
printf("%s %d\n",stringSeperate[i], freq[i]);
3. Check the length of input in order not to cause buffer overrun. Change
if ( i < n ) {
in read_input to
if ( i < n - 1 ) {
in order to make room for terminating null-characteer.

Related

Need to sort a string input by the most frequent characters first in C (qsort)

I managed to sort it alphabetically but I need to sort it by the most frequent characters first after that. Since I'm new to C programming Im not sure if this alphabetical sort is needed. Also I thought about using a struct but not sure how to do the whole process with it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int cmpfunc(const void *a, const void *b) {
return *(char*)a - *(char*)b;
}
void AlphabetOrder(char str[]) {
qsort(str, (size_t) strlen(str), (size_t) sizeof(char), cmpfunc);
printf("%s\n", str);
}
void Max_Occurring(char *str)
{
int i;
int max = 0;
int freq[256] = {0};
for(i = 0; str[i] != '\0'; i++)
{
freq[str[i]] = freq[str[i]] + 1;
}
for(i = 0; i < 256; i++)
{
if(freq[i] > freq[max])
{
max = i;
}
}
printf("Character '%c' appears %d times", max, freq[max], str);
}
int main() {
char str1[20];
printf("Enter a string: ");
scanf("%s", &str1);
AlphabetOrder(str1);
Max_Occurring(str1);
return 0;
}
I wrote you a frequency sorter using the idea that #WeatherVane mentioned:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct cfreq {
unsigned char c;
int freq;
};
int freqcmp(const void *a, const void *b) {
struct cfreq *a2 = (struct cfreq *) a;
struct cfreq *b2 = (struct cfreq *) b;
if(a2->freq < b2->freq) return -1;
if(a2->freq == b2->freq) return 0;
return 1;
}
int freqcmpdesc(const void *a, const void *b) {
return -freqcmp(a, b);
}
void FrequencyOrder(const char str[]) {
struct cfreq cfreqs[256];
for(int i = 0; i < sizeof(cfreqs) / sizeof(*cfreqs); i++) {
cfreqs[i].c = i;
cfreqs[i].freq = 0;
}
for(int i = 0; str[i]; i++) cfreqs[str[i]].freq++;
qsort(cfreqs, sizeof(cfreqs) / sizeof(*cfreqs), sizeof(*cfreqs), freqcmpdesc);
for(int i = 0; i < sizeof(cfreqs) / sizeof(*cfreqs); i++) {
if(cfreqs[i].freq) printf("%c", cfreqs[i].c);
}
printf("\n");
}
int main() {
char str1[20];
printf("Enter a string: ");
scanf("%s", &str1);
FrequencyOrder(str1);
return 0;
}
and here is a sample session (note: output is not deterministic for letters with same frequency):
Enter a string: buzz
zbu
If you want duplicate letters in the output then replace the print with a loop along these lines:
while(cfreqs[i].freq--) printf("%c", cfreqs[i].c);
Im not sure if this alphabetical sort is needed.
It is not needed, yet if done, Max_Occurring() can take advantage of a sorted string.
Since the string is sorted before calling Max_Occurring(), compute the max occurring via a count of adjacent repetitions of each char.
// Untested illustrative code.
// str points to a sorted string.
void Max_Occurring(const char *str) {
char max_ch = '\0';
size_t max_occurence = 0;
char previous = '\0';
size_t occurrence = 0;
while (*str) {
if (*str == previous) {
occurrence++;
} else {
occurrence = 1;
}
if (occurrence > max_occurence) {
max_occurence = occurrence;
max_ch = *str;
}
previous = *str;
str++;
}
printf("Character '%c' appears %zu times", max_ch, max_occurence);
}
In the case of multiple characters with the same max occurrence, this code only reports one max.
Avoid buffer overflow
Do not use scanf("%s"... without a width limit.
Tip: enable all warnings to save time and see the problem of using &str1 when str1 should be used.
char str1[20];
...
// scanf("%s", &str1);
scanf("%19s", str1);
Avoid a negative index
If still wanting to for a frequency table, watch out for the case when char is signed and code use str[i] < 0 to index an array.
Instead:
const unsigned char *ustr = (const unsigned char *) str;
size_t freq[UCHAR_MAX + 1] = {0};
for(size_t i = 0; ustr[i] != '\0'; i++) {
freq[ustr[i]]++;
}
Here's another alternative that may be simpler.
void freqOrder( char *p ) {
#define ASCIIcnt 128 // 7bit ASCII
// to count occurences of each character
int occur[ ASCIIcnt ];
memset( occur, 0, sizeof occur );
int maxCnt = 0; // remember the highest count
// do the counting
for( ; *p; p++ )
if( ++occur[ *p ] > maxCnt )
maxCnt = occur[ *p ];
// output most frequent to least frequen
for( ; maxCnt; maxCnt-- )
for( int i = 0; i < ASCIIcnt; i++ )
if( occur[i] == maxCnt )
while( occur[i]-- )
putchar( i );
putchar( '\n' );
}
int main( void ) {
freqOrder( "The quick brown fox jumps over the lazy dog" );
return 0;
}
Output
' ooooeeehhrruuTabcdfgijklmnpqstvwxyz'

Is my usage of fgets() and strtok() incorrect for parsing a multi-line input?

I'm writing an implementation of the Moore Voting algorithm for finding the majority element (i.e. the element which occurs more than size/2 times) in an array. The code should return the majority element if it exists or else it should return -1. Now my version of the majorityElement(int size, int arr[]) seems to work perfectly fine if I directly hardcode the integer array in the main() function and invoke it from there.
int majorityElement(int size, int arr[])
{
int majorityindex = 0;
int votes = 1;
int index;
for (index = 1; index < size; index++)
{
if (arr[index] == arr[majorityindex])
votes++;
else
votes--;
if (votes == 0)
{
majorityindex = index;
votes = 1;
}
}
int count = 0;
int i;
for (i = 0; i < size; i++)
{
if(arr[majorityindex] == arr[i])
count++;
}
if (count > (size/2))
return arr[majorityindex];
return -1;
}
However, I'm facing some issues if I try to read an input stream like these:
2
5
3 1 3 3 2
3
1 2 3
The first line of the input contains the number of test cases. The first line of the test case will be the size of the array and the second line will be the elements of the array.
I tried to read the input stream from within the main() function like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
int majorityElement(int size, int arr[]);
int main()
{
char buf[3];
fgets(buf, MAX, stdin);
int n = atoi(buf);
char a[3];
char b[MAX];
int i;
int count;
int* num;
for (i = 0; i < n; i++)
{
count = 0;
fgets(a, MAX, stdin);
fgets(b, MAX, stdin);
int x = atoi(a);
char* num[x];
int arr[x];
int k = 0;
char* token = strtok(b, " ");
while (token != NULL)
{
num[k] = token;
arr[k] = atoi(num[k]);
token = strtok(NULL, " ");
k++;
}
printf("%d\n", majorityElement(x, arr));
}
return 1;
}
I took the size of buf[] and a[] as 3 during declaration as they must have sufficient space for the \n character read by fgets() as well as the terminating \0 character. As far as I know, the atoi() function ignores the \n character while converting the character array (string) into an integer. I tried to store the first entry of the input (i.e. the number of entries) in a character array buf, converted it into a string and stored it in a variable n. Similarly, I tried to obtain the size of a test array in a variable x and the test arrays (second line of test case) in an integer array arr. While buf and n seem to obtain the correct values in all cases, I'm not quite sure about arr. I'm aware that fgets() leaves a terminal \n character and that might be causing some havoc during tokenization using strtok, although I can't finger at why. I tried submitting this code on GeeksForGeeks. It gives absolutely correct outputs for the sample test case:
2
5
3 1 3 3 2
3
1 2 3
that is
3
-1
However, when I try to "submit" my solution it says:
Possibly your code doesn't work correctly for multiple test-cases (TCs).
The first test case where your code failed:
Input:
4
1 2 2 1
Its Correct output is:
-1
And Your Code's output is:
1
I can't seem to make sense of this. If I manually write this in stdin:
1
4
1 2 2 1
the code outputs
-1
which is indeed the correct solution. This doesn't match with the output claimed during the submission i.e. 1. So I'm not really sure where I'm going wrong. Have I used fgets() or strtok() incorrectly in the main() function? Or is it something else?
Updated the main() function according to suggestions in the comments.
int main()
{
char buf[MAX];
fgets(buf, MAX, stdin);
int n = atoi(buf);
char a[MAX];
char b[MAX];
int i;
int count;
int* num;
for (i = 0; i < n; i++)
{
count = 0;
fgets(a, MAX, stdin);
fgets(b, sizeof(a), stdin);
a[sizeof(a)-1] = '\0';
b[sizeof(b)-1] = '\0';
int x = atoi(a);
int arr[x];
int k = 0;
char* token = strtok(b, " ");
while (token != NULL)
{
if (k > x)
break;
arr[k] = atoi(token);
token = strtok(NULL, " ");
k++;
}
printf("%d\n", majorityElement(x, arr));
}
return 1;
}
As pointed out by #Vlad, the MAX was set too low in my original array. The question says that the number of entries in an array is upper bounded by 10^7 and each array entry is upper bounded by 10^6 (7 digits). So MAX needs to be of the order 10^8. According to the suggestions in the comments, I'm now using dynamic allocation instead of variable length arrays.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 10000000
int majorityElement(int size, int arr[])
{
int majorityindex = 0;
int votes = 1;
int index;
for (index = 1; index < size; index++)
{
if (arr[index] == arr[majorityindex])
votes++;
else
votes--;
if (votes == 0)
{
majorityindex = index;
votes = 1;
}
}
int count = 0;
int i;
for (i = 0; i < size; i++)
{
if(arr[majorityindex] == arr[i])
count++;
}
if (count > (size/2))
return arr[majorityindex];
return -1;
}
int main()
{
char* buf = calloc (MAX, sizeof(char));
fgets(buf, MAX, stdin);
int n = atoi(buf);
char* a = calloc (MAX, sizeof(char));
char* b = calloc(MAX, sizeof(char));
int i;
for (i = 0; i < n; i++)
{
fgets(a, MAX, stdin);
fgets(b, MAX, stdin);
a[strlen(a)-1] = '\0';
b[strlen(b)-1] = '\0';
int x = atoi(a);
int *arr = calloc(x, sizeof(int));
int k = 0;
char* token = strtok(b, " ");
while (token != NULL)
{
if (k > x)
break;
arr[k] = atoi(token);
token = strtok(NULL, " ");
k++;
}
printf("%d\n", majorityElement(x, arr));
free(arr)
}
free(buf);
free(a);
free(b);
return 1;
}
If I set MAX to 10^7 then the code passes all the test cases and is accepted for submission. However, if I set MAX to 10^8 (as required), I get a segmentation fault. How to overcome this?
Your program has several drawbacks.
For example within the function main there are unused variables declared like
int count;
int* num;
The function does take into account that -1 can be a valid value of the array.
There is a problem with the number of elements that can be specified in a test. It is a very big number (according to the description 1 <= N <= 10000000). So the value of MAX equal to 100 is too low. As a result the data can be read incorrectly and not completely. Also there can occur problems with the variable length arrays.
There is no need to use the function fgets because each integer number can be read using scanf.
I could suggest the following solution. Try it and see whether it will pass the tests.
#include <stdio.h>
#include <stdlib.h>
size_t majorityElement( const int a[], size_t n )
{
size_t majority_index = 0;
for ( size_t i = 1, votes = 1; i < n; i++ )
{
if ( a[majority_index] == a[i] )
{
++votes;
}
else
{
--votes;
}
if ( votes == 0 )
{
majority_index = i;
++votes;
}
}
size_t count = 0;
for ( size_t i = 0; i < n; i++ ) count += a[i] == a[majority_index];
return n / 2 < count ? majority_index : n;
}
int main(void)
{
size_t n = 0;
scanf( "%zu", &n );
for ( size_t i = 0; i < n; i++ )
{
size_t m = 0;
scanf( "%zu", &m );
if ( m != 0 )
{
int *a = calloc( m, sizeof( int ) );
for ( size_t j = 0; j < m; j++ ) scanf( "%d", a + j );
size_t majority_index = majorityElement( a, m );
printf( "%d\n", majority_index == m ? -1 : a[majority_index] );
free( a );
}
}
return 0;
}
If it will not pass the tests then it seems there is a bug in tests.:)
Or if the function return type may not be changed then the function definition can look like
int majorityElement( const int a[], size_t n )
{
size_t majority_index = 0;
for ( size_t i = 1, votes = 1; i < n; i++ )
{
if ( a[majority_index] == a[i] )
{
++votes;
}
else
{
--votes;
}
if ( votes == 0 )
{
majority_index = i;
++votes;
}
}
size_t count = 0;
for ( size_t i = 0; i < n; i++ ) count += a[i] == a[majority_index];
return n / 2 < count ? a[majority_index] : -1;
}

String array prints out trash values

So I have an assignment where I should delete a character if it has duplicates in a string. Right now it does that but also prints out trash values at the end. Im not sure why it does that, so any help would be nice.
Also im not sure how I should print out the length of the new string.
This is my main.c file:
#include <stdio.h>
#include <string.h>
#include "functions.h"
int main() {
char string[256];
int length;
printf("Enter char array size of string(counting with backslash 0): \n");
/*
Example: The word aabc will get a size of 5.
a = 0
a = 1
b = 2
c = 3
/0 = 4
Total 5 slots to allocate */
scanf("%d", &length);
printf("Enter string you wish to remove duplicates from: \n");
for (int i = 0; i < length; i++)
{
scanf("%c", &string[i]);
}
deleteDuplicates(string, length);
//String output after removing duplicates. Prints out trash values!
for (int i = 0; i < length; i++) {
printf("%c", string[i]);
}
//Length of new string. The length is also wrong!
printf("\tLength: %d\n", length);
printf("\n\n");
getchar();
return 0;
}
The output from the printf("%c", string[i]); prints out trash values at the end of the string which is not correct.
The deleteDuplicates function looks like this in the functions.c file:
void deleteDuplicates(char string[], int length)
{
for (int i = 0; i < length; i++)
{
for (int j = i + 1; j < length;)
{
if (string[j] == string[i])
{
for (int k = j; k < length; k++)
{
string[k] = string[k + 1];
}
length--;
}
else
{
j++;
}
}
}
}
There is a more efficent and secure way to do the exercise:
#include <stdio.h>
#include <string.h>
void deleteDuplicates(char string[], int *length)
{
int p = 1; //current
int f = 0; //flag found
for (int i = 1; i < *length; i++)
{
f = 0;
for (int j = 0; j < i; j++)
{
if (string[j] == string[i])
{
f = 1;
break;
}
}
if (!f)
string[p++] = string[i];
}
string[p] = '\0';
*length = p;
}
int main() {
char aux[100] = "asdñkzzcvjhasdkljjh";
int l = strlen(aux);
deleteDuplicates(aux, &l);
printf("result: %s -> %d", aux, l);
}
You can see the results here:
http://codepad.org/wECjIonL
Or even a more refined way can be found here:
http://codepad.org/BXksElIG
Functions in C are pass by value by default, not pass by reference. So your deleteDuplicates function is not modifying the length in your main function. If you modify your function to pass by reference, your length will be modified.
Here's an example using your code.
The function call would be:
deleteDuplicates(string, &length);
The function would be:
void deleteDuplicates(char string[], int *length)
{
for (int i = 0; i < *length; i++)
{
for (int j = i + 1; j < *length;)
{
if (string[j] == string[i])
{
for (int k = j; k < *length; k++)
{
string[k] = string[k + 1];
}
*length--;
}
else
{
j++;
}
}
}
}
You can achieve an O(n) solution by hashing the characters in an array.
However, the other answers posted will help you solve your current problem in your code. I decided to show you a more efficient way to do this.
You can create a hash array like this:
int hashing[256] = {0};
Which sets all the values to be 0 in the array. Then you can check if the slot has a 0, which means that the character has not been visited. Everytime 0 is found, add the character to the string, and mark that slot as 1. This guarantees that no duplicate characters can be added, as they are only added if a 0 is found.
This is a common algorithm that is used everywhere, and it will help make your code more efficient.
Also it is better to use fgets for reading input from user, instead of scanf().
Here is some modified code I wrote a while ago which shows this idea of hashing:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define NUMCHAR 256
char *remove_dups(char *string);
int main(void) {
char string[NUMCHAR], temp;
char *result;
size_t len, i;
int ch;
printf("Enter char array size of string(counting with backslash 0): \n");
if (scanf("%zu", &len) != 1) {
printf("invalid length entered\n");
exit(EXIT_FAILURE);
}
ch = getchar();
while (ch != '\n' && ch != EOF);
if (len >= NUMCHAR) {
printf("Length specified is longer than buffer size of %d\n", NUMCHAR);
exit(EXIT_FAILURE);
}
printf("Enter string you wish to remove duplicates from: \n");
for (i = 0; i < len; i++) {
if (scanf("%c", &temp) != 1) {
printf("invalid character entered\n");
exit(EXIT_FAILURE);
}
if (isspace(temp)) {
break;
}
string[i] = temp;
}
string[i] = '\0';
printf("Original string: %s Length: %zu\n", string, strlen(string));
result = remove_dups(string);
printf("Duplicates removed: %s Length: %zu\n", result, strlen(result));
return 0;
}
char *remove_dups(char *str) {
int hash[NUMCHAR] = {0};
size_t count = 0, i;
char temp;
for (i = 0; str[i]; i++) {
temp = str[i];
if (hash[(unsigned char)temp] == 0) {
hash[(unsigned char)temp] = 1;
str[count++] = str[i];
}
}
str[count] = '\0';
return str;
}
Example input:
Enter char array size of string(counting with backslash 0):
20
Enter string you wish to remove duplicates from:
hellotherefriend
Output:
Original string: hellotherefriend Length: 16
Duplicates removed: helotrfind Length: 10

Function that extracts words from text ( array of chars ) and put them in 2 dimensions array

I'm learning C and have some struggles.I have to make a program , which becomes a text (max 80 chars) and put the words from text in a char words[80][80] (every word must be only single time in this array! it is also defined as global) and count of times every word comes in the text in a int count[] (Index must be same as this from words[][]).
The function is called int extract_and_count(char *source,int *count).
I wrote some code ,but I'm not sure how exactly to implement this function.Can someone help me?
I'm also new to stackoverflow so if I have made any mistake, sorry.
Thats some of the code but its not to the end:
int extract_and_count(char *source,int *count){
char token[80][80];
char *p;
int i = 0;
p = strtok(source, " ");
while( p != NULL ){
strcpy(token[i],p);
printf("%s\n",*(token+i));
i++;
p = strtok(NULL , " ");
}
char word;
int value = 0, j;
for(i = 0 ; i < 80 ; i++){
word = token[i];
for(j = 0 ; j < 80 ; j++){
if(strcmp(word,token[i])==0){
value++;
}
}
}
return 1;
}
You need to check if a word has been found already. If so, just increment the global counter. Otherwise, copy the new word to the global array of strings.
Something like:
#include <stdio.h>
#include <string.h>
// Global variables to hold the results
char word[80][81];
int count[80] = { 0 };
int extract_and_count(char *source,int *strings_cnt){
char token[80][81];
char *p;
int i = 0;
// Find all words in the input string
p = strtok(source, " ");
while( p != NULL ){
strcpy(token[i],p);
// printf("%s\n",*(token+i));
i++;
p = strtok(NULL , " ");
}
// Find unique words and count the number a word is repeated
*strings_cnt = 0;
int j,k;
// Iterator over all words found in the input string
for(j = 0 ; j < i ; j++){
// Check if the word is already detected once
int found = 0;
for(k = 0 ; k < *strings_cnt ; k++){
if (strcmp(word[k], token[j]) == 0)
{
// The word already exists - increment count
found = 1;
count[k]++;
break;
}
}
if (!found)
{
// New word - copy it and set count to 1
strcpy(word[*strings_cnt], token[j]);
count[*strings_cnt] = 1;
(*strings_cnt)++;
}
}
return 1;
}
int main(void)
{
char s[] = "c language is difficult c is also fun";
int c, i;
printf("Searching: %s\n", s);
extract_and_count(s, &c);
printf("Found %d different words\n", c);
for (i=0; i<c; i++)
{
printf("%d times: %s\n", count[i], word[i]);
}
return 0;
}
Output:
Searching: c language is difficult c is also fun
Found 6 different words
2 times: c
1 times: language
2 times: is
1 times: difficult
1 times: also
1 times: fun
Above I tried to follow your codes style but I like to add these comments:
1) You don't really need the token array. The first loop can be changed so that it updates the final result directly.
2) Don't use global variable
3) The code can't handle normal separators like , . : and so on
4) You should put the word and the count into a struct.
Taken comment 1,2 and 4 in to consideration, the code could be:
#include <stdio.h>
#include <string.h>
// Global variables to hold the results
struct WordStat
{
char word[81];
int count;
};
int extract_and_count(char *source,int *strings_cnt, struct WordStat* ws, int max){
char *p;
int i = 0;
int k;
*strings_cnt = 0;
// Find all words in the input string
p = strtok(source, " ");
while( p != NULL ){
// Check if the word is already detected once
int found = 0;
for(k = 0 ; k < *strings_cnt ; k++){
if (strcmp(ws[k].word, p) == 0)
{
// The word already exists - increment count
found = 1;
ws[k].count++;
break;
}
}
if (!found)
{
// New word - copy it and set count to 1
strcpy(ws[*strings_cnt].word, p);
ws[*strings_cnt].count = 1;
(*strings_cnt)++;
}
i++;
p = strtok(NULL , " ");
}
return 1;
}
#define MAX_WORDS 80
int main(void)
{
struct WordStat ws[MAX_WORDS];
char s[] = "c language is difficult c is also fun";
int c, i;
printf("Searching: %s\n", s);
extract_and_count(s, &c, ws, MAX_WORDS);
printf("Found %d different words\n", c);
for (i=0; i<c; i++)
{
printf("%d times: %s\n", ws[i].count, ws[i].word);
}
return 0;
}
while( p != NULL ){
strcpy(token[i],p);
printf("%s\n",*(token+i));
i++;
p = strtok(NULL , " "); --> here you are just splitting the words
}
Now token will contain all the words in splitted manner, not as per your requirement of "each word only once". You can compare and copy the unique words to another array and in the same loop, you can count and update the count array.
Note: You should not use one counter variable on the whole, the array of counter only shall be used to count the words.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define NUM_OF_WORDS_MAX 80
#define MAX_WORD_LENGTH 79
#define S_(x) #x
#define S(x) S_(x) //number literal convert to string
char words[NUM_OF_WORDS_MAX][MAX_WORD_LENGTH+1];
int Words_entry = 0;
static inline int hash(const char *str){
return (tolower(*str) - 'a')*3;//3:(NUM_OF_WORDS_MAX / 26), 26 : a-z
}
char *extract(char **sp){//extract word
char *p = *sp;
while(*p && !isalpha(*p))//skip not alpha
++p;
if(!*p)
return NULL;
char *ret = p;//first word
while(*p && isalpha(*p))//skip alpha
++p;//*p = tolower(*p);
if(!*p){
*sp = p;
} else {
*p = '\0';
*sp = ++p;//rest
}
return ret;
}
int extract_and_count(char *source, int *count){
char *sp = source;
char *word;
int word_count = 0;
while(word = extract(&sp)){
if(Words_entry == NUM_OF_WORDS_MAX){
fprintf(stderr, "words table is full.\n");
return word_count;
}
int index = hash(word);
while(1){
if(*words[index]){
if(strcasecmp(words[index], word) == 0){//ignore case
++count[index];
break;
}
if(++index == NUM_OF_WORDS_MAX){
index = 0;
}
} else {
strcpy(words[index], word);
count[index] = 1;
++Words_entry;
break;
}
}
++word_count;
}
return word_count;
}
int main(void){
int count[NUM_OF_WORDS_MAX] = {0};
char text[MAX_WORD_LENGTH+1];
while(1==scanf("%" S(MAX_WORD_LENGTH) "[^\n]%*c", text)){//end if only enter press.
extract_and_count(text, count);
}
//print result
for(int i = 0; i < NUM_OF_WORDS_MAX; ++i){
if(*words[i]){
printf("%s : %d\n", words[i], count[i]);
}
}
return 0;
}

How to replace every specific character to another in a string C?

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;
}

Resources