3

Question: Define an int function that removes all consecutive vowel repetitions from a string. The function should return the number of vowels removed and present the string without duplicates.

I am PT so Vogais is Vowels; Digite uma String is Write one String. A String sem duplicados fica assim ' %s ' e foram retiradas %d vogais is The string without duplicates is ' %s ' and where removed %d vowels.

Explanation: In portuguese we have some words with two consecutive vowels like: coordenador, coordenação (chqrlie example). But in thouse cases should be ignored in the context of this problem.

Problem: When I test a string like 'ooooo' it says the string without duplicate vogals is 'oo' and where removed 3 vowels. But it should be 'o' and 4 vowels removed. Another example with error is 'Estaa e umaa string coom duuuplicadoos', I am getting ' Esta e uma string com duplcdos ' and 8 vowels removed.

Note: This is a simple question so there isn't need to complicate. It only askes the consecutive duplicate vowels. The cases 'oOoO' -> 'oO' ,'abAb'->'abAb','abab' -> 'ab','aba'-> 'aba',... are in another chapter XD.

int Vogais(char *s) {
    if (*s == 'A' || *s == 'a' || *s == 'E' || *s == 'e'
     || *s == 'I' || *s == 'i' || *s == 'O' || *s == 'o'
     || *s == 'U' || *s == 'u') return 1;
    return 0;
}

int retiraVogaisRep(char *s) {
    int res = 0;
    for (int i = 0; i < strlen(s); i++) {
        for (int j = i + 1; s[j] != '\0'; j++) {
            if (s[i] == s[j] && Vogais(&s[j]) == 1) {
                res++;
                for (int k = j; s[k] != '\0'; k++) {
                    s[k] = s[k + 1];
                }
            }
        }
    }
    return res;
}

int main() {
    char s[38];
    printf("Digite uma String:\n");
    scanf("%[^\n]", s);
    int res = retiraVogaisRep(s);
    printf("A String sem duplicados fica assim ' %s ' e foram retiradas %d vogais.\n", s, res);
    return 0;
}
IvoLims
  • 87
  • 9

5 Answers5

4

Your code is too complicated: there is no need for nested loops for this task and you do not set the null terminator when shortening the string.

Here is a simpler version:

#include <stdio.h>
#include <string.h>

int retiraVogaisRep(char *s) {
    int i, j;      // use 2 running indices
    char c, last = 0;
    for (i = j = 0; (c = s[i]) != '\0'; i++) {
        if (c != last || !strchr("aeiouAEIOU", c))
            s[j++] = last = c;
    }
    s[j] = '\0';   // set the null terminator
    return i - j;  // return the number of bytes removed
}

int main() {
    char s[100];

    printf("Digite uma String:\n");

    // read the user input safely with `fgets()`
    if (!fgets(s, sizeof s, stdin))
        return 1;

    // strip the trailing newline if any
    s[strcspn(s, "\n")] = '\0';

    // remove duplicate consecutive vowels
    int res = retiraVogaisRep(s);

    printf("A String sem duplicados fica assim ' %s ' e foram retiradas %d vogais.\n", s, res);
    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
2

Remove consecutive duplicate vowels

You should use tolower function from ctype.h to check for vowels, that include the letter 'y', see below working code:

You can store previous character in prev and compare it to the current character, as you are case insensitive you store the tolower version.

#include <string.h>
#include <stdio.h>
#include <ctype.h>

int Vogais(char c){
    return (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'y') ;
}

int retiraVogaisRep (unsigned char *s){
    if (*s == NULL)
        return 0;
        
    unsigned char t[256];
    memset(t, 0, sizeof(t));
    int res = 0;
    int j = 0;
    t[0] = s[0];
    char prev = tolower(s[0]);
    int len = strlen(s);

    for (int i = 1; i < len; i++) {
        char c = tolower(s[i]);
        if (Vogais(c) && c == prev)
            ++res;
        else
            t[j++] = s[i];
        prev = c;

    }
    memcpy(s, t, sizeof(t));
    return res;
}

int main(){
    char s[256];
    printf("Digite uma String:\n");
    scanf("%255[^\n]", s);
    int res = retiraVogaisRep(s);
    printf("Da String ' %s ' podem ser retiradas %d vogais.\n", s,res);
    return 0;
}
Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81
  • Nope try this example 'Estaa e umaa string coom duuuplicadoos' should be 'Esta e uma string com duplicados' and I am getting with your code 'Esta um string' – IvoLims Mar 24 '21 at 23:06
  • I'm afraid you should not assume that the argument string has a size of 20 bytes. You should replace the string characters in place and make sure you null terminate the shortened string. `scanf("%20[^\n]", s);` should be `scanf("%19[^\n]", s);`. The input buffer should probably be made larger. `if (*s == NULL)` is incorrect: use `if (*s == '\0')` instead. – chqrlie Mar 24 '21 at 23:08
  • @IvoLims: your example string is too long for the 20 byte buffer in `main()`. Your code has undefined behavior for overlong input. – chqrlie Mar 24 '21 at 23:09
  • Your approach will consider "Oo" to have a duplicate vowel: I'm not sure this is expected, and the OP's code certainly does not do that. – chqrlie Mar 24 '21 at 23:12
  • In portuguese we don't have words with two consecutive vowels. And if thouse cases exist, they should be ignored in the context of this problem. – IvoLims Mar 24 '21 at 23:17
  • @chqrlie Isn't "Oo" has a duplicate vowel? The OP's function `Vogais` defines both `O` and `o`... – user14063792468 Mar 24 '21 at 23:18
  • @user14063792468: the OP's code tries to remove duplicated characters that are uppercase or lowercase vowels. `O` and `o` is not the same character. By duplicate, it seems the OP means identical consecutive letters. – chqrlie Mar 24 '21 at 23:22
  • @chqrlie Just looked closer on the OP's code. You are right. He seem to remove only identical characters. I should edit my question. – user14063792468 Mar 24 '21 at 23:28
  • Try again with a buffer of 256 chars. See my edit – Antonin GAVREL Mar 24 '21 at 23:54
  • 1
    The number in the `scanf` conversion is the maximum number of characters to use for the conversion, so it does not include the null terminator. Hence you must write `scanf("%255[^\n]", s);` It is simpler to use `fgets()`. – chqrlie Mar 25 '21 at 00:44
  • I was thinking about this and then I tested with 20* letters as input and there was no problem, how comes? (*original buffer size from OP) – Antonin GAVREL Mar 25 '21 at 00:46
  • 1
    You were lucky... The stack might be aligned on 8 or 16 byte boundaries so there is space for some extra bytes after the array before you touch anything risky such as the caller's frame pointer or the return address. see https://godbolt.org/z/9EosdMqxz – chqrlie Mar 25 '21 at 00:48
  • `for (int i = 1; i < strlen(s); i++) {` obliges code to compute `strlen(s)` each iteration. Each call cost O(string_length) time. Compiler cannot, in this case, assume `strlen(s)` returns the same value each time. – chux - Reinstate Monica Mar 25 '21 at 01:14
  • It is true if you compile without optimization flags. Any compiler with optimization flag would store strlen(s) in a register, already tested a while ago with gcc. – Antonin GAVREL Mar 25 '21 at 01:17
  • @AntoninGAVREL: I'm afraid you are mistaken. In this particular case, you modify the array pointed to by `s` and the compiler cannot easily determine that `strlen(s)` will stay the same for each call. Recomputing `strlen(s)` at each iteration is a classic mistake and expecting the optimizer to correct it is a bad assumption. Furthermore this idiom sets a bad example for beginners. – chqrlie Mar 25 '21 at 13:45
  • I modify t, not s, I just checked on godbolt with O3, but I agree for the bad example it sets – Antonin GAVREL Mar 25 '21 at 14:13
2

The question tag is C, but I will not post the actual code here.

The pseudocode:

function is_vowel(int c) {...}

start loop c = <src>
 if next_char is past the last char then quit loop;
 if is_vowel(c) and c == next_char and is_vowel(next_char)
  then continue;
 else
  copy c to <dst>

You should elaborate on this, as the above is possibly having small issues. Nevertheless, I think this answer is somewhat shorter and gives an insight.

Update

The above is definitly have an issue, in that the next char does not copied to the output. The mistake is easy to correct, so I will leave it up to OP.

Update

Edited above code to indicate that OP wants to remove only identical duplicates. So, the case of a charcter is important.

user14063792468
  • 839
  • 12
  • 28
2

Rather than a triple nested loop, consider a single walk down the string, looking for repeats.

#include <stdio.h>
#include <ctype.h>

int Vogais(unsigned char s) {
    if (s == 'A' || s == 'a' || s == 'E' || s == 'e'
     || s == 'I' || s == 'i' || s == 'O' || s == 'o'
     || s == 'U' || s == 'u') return 1;
    return 0;
}

int retiraVogaisRep(char *s) {
  unsigned char *us = (unsigned char *) s;
  unsigned char *dest = us;
  int res = 0;
  int prior = EOF;
  while (*us) {
    while (toupper(*us) == prior) {
      us++;
      res++;
    }
    prior = Vogais(*us) ? toupper(*us) : EOF;
    *dest++ = *us++;
  }
  *dest = '\0';
  return res;
}

int main() {
  char buf[100] = "OoFreedaa";
  printf("%d\t", retiraVogaisRep(buf));
  printf("<%s>\n", buf);
  return 0;
}

Output

3   <OFreda>
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

Retaining the uppercase, using the Kernighan-copy


#include <stdio.h>
#include <string.h>
#include <ctype.h>

size_t remove_duplicate_vowels(char *str)
{
int old,new;
size_t dst,src;

old = 0;
for(dst=src=0; str[dst] = str[src]; old=new, src++ ) {
        new = toupper( str[dst] );
        if ( !strchr( "AEIOU",  new )) { // Not a vowel
                dst++; continue;
                }
        if ( new != old ) { // Not a repetition
                dst++; continue;
                }
        }

return src - dst;
}

int main(int argc, char **argv)
{
char test[] = "Aaa bbBb CccCC d eEeee!";
char *arg;
size_t ret;

arg = argv[1] ?  argv[1] : test;

ret = remove_duplicate_vowels(arg);

fprintf(stderr, "[%zu]: %s\n", ret, arg);

return 0;
}
wildplasser
  • 43,142
  • 8
  • 66
  • 109
  • Very strange that I don't get any credits for this, since it is the correct&simplest solution. – wildplasser Mar 25 '21 at 23:57
  • Unfortunally , I dont vote down competitors. But the gap is huge. – wildplasser Mar 26 '21 at 01:10
  • Although your function works, I am not allowed to change the type of the function in the context of the problem and I only marked an example of a answear for my question. – IvoLims Mar 26 '21 at 14:27
  • What do you mean by `change the type of the function` ? – wildplasser Mar 26 '21 at 15:05
  • The signature of the function shouldn't be change. – IvoLims Apr 01 '21 at 10:20
  • Aha! You mean the `int` vs `size_t` thing? It is trivial: just change `size_t` back to `int`. (but: `size_t` is marginally *better* in this case; using ints for sizes is just a bad habit) – wildplasser Apr 01 '21 at 10:44
  • I know that, but my teachers insist to use that way because blah,blah,...And the other responses were quicker to appear. But your answear is cool too, thanks for helping (I already upvoted this one XD) – IvoLims Apr 01 '21 at 11:37