Tyler Gee
Tyler Gee

Reputation: 25

Counting unique characters in string given by user C programming

Write a program that prompts the user to enter a string and prints the goodness of the string. The goodness of a string in general, is calculated in the following way: If the string contains any letters except for 0 or 1, then its goodness is 0. Otherwise, its goodness is the number of 1's in the string.

int numZeros, numOnes, i;
char sent[50];
i = 0;


printf("Enter a string with no spaces: ");
scanf(" %s", sent);

for(i=0; i != '\0'; ++i){
    if(sent[i] == '0'){
        ++numZeros;
    }
    else if(sent[i] != '\0'){
        ++numOnes;
    }
}


if((numOnes == 0) && (numZeros == 0)){
    printf("\nGoodness of the input is 0\n");
}
else if((numZeros > 0) && (numOnes == 0)){
    printf("\nGoodness of the input is 0\n");
}
else{
    printf("\nGoodness of the input is %d\n", numOnes);
}

return 0;

Could someone please explain why in the last else statement, when i call and try to display the value of variable "numOnes" i will get some obsure number, even if I input a specific number of 1's into a sentence?

Upvotes: 1

Views: 2141

Answers (3)

Kaz
Kaz

Reputation: 58617

We can define a property called "almost-goodness" recursively, and write a recursive solution. C strings support recursion very well because if s is a non-empty C string, then s + 1 (one byte higher address) is also a C string, representing the suffix which remains after the first character of s.

/* almost_goodness returns -1 if the string contains
   bad characters, otherwise the count of 1's. */

static int almost_goodness(const char *str)
{
   switch (*str) {
   case 0: /* null terminator: empty string */
     return 0;
   case '0':
   case '1':
     {
        int ag_rest = almost_goodness(str + 1);
        return ag_rest < 0 ? ag_rest : (*str == '1') + ag_rest;
     }
   default:
     return -1;
   }
}

int goodness(const char *str)
{
   int ag = almost_goodness(str);
   return ag < 0 ? 0 : ag;
}

Almost-goodness is defined as this:

  • If the string is empty, its almost-goodness is zero.

  • If the first character of the string is other than 1 or 0, its almost-goodness is -1.

  • If the almost-goodness of the rest of the string is -1, the almost-goodness is -1.

  • Otherwise, if the first character is 1, the almost-goodness is 1 plus the almost-goodness of the rest of the string.

  • Otherwise, the first character must be 0, and the almost-goodness is that of the rest of the string.

Goodness is derived from almost-goodness by treating -1 as 0.


Here is something better: a tail recursive goodness, with the passage of an explicit accumulator. This not allows the compiler to optimize it into a loop which means that it doesn't require an amount of stack space proportional to the length of the incoming string. As a bonus, the code is clearer, which is often the opposite with recursion rewritten to pass an accumulator:

static int almost_goodness(const char *str, int goodness_acc)
{
  if (goodness_acc < 0)
    return goodness_acc;

  switch (*str) {
  case 0:
    return goodness_acc;
  case '1':
    return almost_goodness(str + 1, 1 + goodness_acc);
  case '0':
    return almost_goodness(str + 1, goodness_acc);
  default:
    return -1;
  }
}

int goodness(const char *str)
{
  int ag = almost_goodness(str, 0);
  return ag < 0 ? 0 : ag;
}

Here the "(almost-)goodness accumulator" parameter goodness_ac represents "the goodness that we know so far about some earlier part of the string that we have already seen before". If this value is -1, we don't have to bother processing the part of the string we are given.


Lastly, here is a purely iterative goodness function:

int goodness(const char *str)
{
  int g = 0;

  for (; *str; str++) {
    switch (*str) {
    case '0':
      break;
    case '1':
      g++;
      break;
    default:
      return 0;
    }
  }

  return g;
}

Very simple: loop through the string, incrementing a counter for every 1, and skipping every 0. If we see any other character, we bail immediately with zero regardless of the value of the counter.

Upvotes: 0

user2736738
user2736738

Reputation: 30926

  • else if( sent[i]!='0') ...

  • Initialize numOnes numZeros.

  • for loop will be for(i=0;sent[i]!='\0';i++)

Also you can simplify the print logic. Calculate the length of the input.

if(numZeroes+numOnes < len )
// Goodness is zero
else
 // Goodness is numOnes

Implementation:

int main()
{
    int numZeros=0, numOnes=0;
    char sent[50];
    printf("Enter a string with no spaces: ");
    scanf(" %s", sent);

    for(int i=0; sent[i]; ++i)
        (sent[i]=='0')?numZeros++:numOnes++;

    printf("Goodness of the input is %d", (numOnes+numZeros<strlen(sent))?0:numOnes);
    return 0;
}

Upvotes: 2

Andr&#233;s R.C.
Andr&#233;s R.C.

Reputation: 488

Try this:

int main(){
    int numZeros = 0, numOnes = 0, i;
    bool ver = true;
    char sent[50];
    i = 0;

    printf("Enter a string with no spaces: ");
    scanf(" %s", sent);

    for(i=0; sent[i] != '\0'; ++i){
        if(sent[i] == '0')
            ++numZeros;
        else if(sent[i] == '1')
            ++numOnes;
        else{
            ver = false;
            break;
        }
    }

    if(ver){
        if((numOnes == 0) && (numZeros == 0))
            printf("\nGoodness of the input is 0\n");
        else if((numZeros > 0) && (numOnes == 0))
            printf("\nGoodness of the input is 0\n");
        else
            printf("\nGoodness of the input is %d\n", numOnes);
    }else{
        printf("\nGoodness of the input is 0\n");
    }

    return 0;
}

You do not need any extra library.

Upvotes: 0

Related Questions