Test whether all characters of the first string also appear in the second

Asked

Viewed 2,736 times

16

I’m trying to do that function with the help of the functions strcmp() and strncmp() and I’m not having much success.

Let’s say I have the string char s[] = "abc" and another string char v[] = "cdeabf" , the output would give 0 (true value) because the characters [a, b and c] belong to v. Otherwise it would give -1 (false value).

If there is a predefined function that helps me, what would it be? And if you could do this function without the help of predefined functions, how would you do it?

  • Do you know the size of the strings? I gave an answer that uses no predefined function except for for strlen (otherwise there would be no way of knowing how long the strings are). But if you already know these values beforehand, then you don’t need to...

  • 1

    @mgibsonbr although I have understood the intention of the comment have to say that it is possible to discover the size of the string without a predefined function, just write a code that does exactly what the strlen does :P Something like that: const char *s; for (s = suaString; *s; ++s); int tamanho = s - suaString;. Just out of curiosity.

  • @bigown haha of course, my fault! What a strlen does is precisely count the characters up to - but not including - the null terminator, make it easy to do by hand if necessary... : P

5 answers

15

As referred to by @mgibsonbr, the C convention is 0 for false, not zero for true.

Using this convention, I would simply:

...
return (strspn(s,aceitar) == strlen(s));

Edit: Summary explanation: according to the manual, chapter of functions on strings:

$ man string.h 
....
size_t strspn(const char *s, const char *accept);
   Calculate the length of the starting segment in the string s that consists 
   entirely of bytes in accept.

we have a function strspn (See details here) that can help:

if all the characters of the initial string are in the list of characters to accept, the function strspn(s,aceitar) return string length s (=all).

For our question, just compare the number of accepted characters with strlen(s)


Thanks to @mgibsonbr for helping with the explanation

  • 4

    Excellent answer! I am offering a reward for it. However, it would be desirable a reference to function strspn (example), and maybe explain what she does.

  • Thanks for the comment and the suggestion to explain better... (I’m always a little clumsy in these things).

  • 1

    The reply @Jjoao :) is very good. Thank you for the explanation! ;)

  • When I saw the initial answer I was amazed, excellent!

  • @mgibsonbr thanks for the link (which I will include in the explanation).

  • Can you put a code showing it working according to what the question asks? Either I don’t understand your answer or it doesn’t work.

  • 2

    I think @bigown is right, look at ideone

  • 1

    @Jorgeb. If that’s right, and I had already done this test, people are voting for a wrong answer that is easily verifiable but no one has bothered before voting. The vote should be given when you know that the solution is good and works, not because she is curious.

  • @bigown I think it was typo. I myself took the test 1000 laps with the 'v' and’s'. Up because the explanation is right.

  • 1

    @Jjoao will be return (strspn(s,v) == strlen(s));?

  • 1

    @Jorgeb. Excuse my confusion: maybe I changed the ids: what I mean is obviously return (strspn(str_a_testar,aceites) == strlen(str_a_testar));

  • @bigown: thank you very much for the remark (I’m an ass!) -- when editing the solution proposals, we don’t see the text of the question at the same time and changed the names. According to your comments, I have modified the ids to be more legible and in accordance with the explanation ids.

  • 1

    @Bigown In fact, the s and the v were changed. I confess that neither tested the code - trusting that a single line of code whose semantics were clear, and the precise documentation, could be understood only with my head. And in fact, the bug was in the variables of less descriptive names, not in the logic of the solution. In the end the biggest mistake was mine, because it was the one who called attention to the answer without checking it more carefully (I will still give the reward to her, however, by the logic itself). Hehe remembered at the time of Knuth :P

  • @mgibsonbr, (1) thank you very much for the +100, until I am touched! (I am a donkey and did not know what was a "reward"). (2) many thanks for sharing with us "Beware of bugs in the above code; I have only proved it correct, not tried it."

Show 9 more comments

11


There’s nothing ready but(see below) it is not difficult to create a function that does this. If I understood what you want, it would be this:

#include <stdio.h>

int contem(char s1[], char s2[]) {
    for (int i = 0; s2[i]; i++) {
        int j;
        for (j = 0; s1[j]; j++) if(s2[i] == s1[j]) break;
        if (s1[j] == '\0') return 0;
    }
    return 1;
}

int main() {
    char s[] = "abc";
    char v[] = "cdeabf";
     if (contem(v, s)) printf("'%s' contém todos os caracteres presentes em '%s'", v, s);
     else printf("'%s' não contém todos os caracteres presentes em '%s'", v, s);
}

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

Here is the solution for those who want to do it in the hand. But there is a way to achieve the same result according to Jjoao’s answer. The function strspn returns the size of string being compared if all characters are present in the other string. So it is easy to identify what is asked in the question. Using ready function would look like this:

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

int main() {
    char s[] = "abc";
    char v[] = "cdeabf";
    printf("'%s' %scontém todos os caracteres presentes em '%s'", v, (strspn(s, v) == strlen(s)) ? "" : "não ", s);
}

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

  • I just didn’t notice something @bigown , what is the role of variable j in this case? Thank you for the answer ;)

  • @Josécunha put some comments. O j is only the control variable of the string. It will vary to walk character by character in string. Does this clarify what you wanted to know? I saw that you had accepted my answer and then switched to the other one that provides a similar solution, so much so that I voted for it. I was just wondering if you did it because then you saw that you preferred the other one or you didn’t want to and you didn’t realize that you can only accept an answer like the one that helped you the most. You can vote on all answers and questions from website once you have enough reputation. [tour]

  • Thanks for clearing @bigown ;) I didn’t know you could just accept a reply xD . Anyway , I really liked the comments and the structure which makes it easy to read! Thanks for the Help! ;)

  • I do not recommend using strlen within loops, as you did in if (j == strlen(s1)) {. strlen is an expensive operation, which takes O(n) time and the compiler can’t always be smart enough to optimize it. In your case it would be better to use s1[j] like you already do in for. Or who knows until you trade those ifs and break for a smart use of goto.

  • @hugomg is right, I did it running and I didn’t even worry about it but I will improve it. I didn’t want to use the goto not to provoke epileptic seizures in some programmers :D

10

How to compare characters is easy (just use the equal operator - ==), my suggestion is to simply scroll through each character of one and the other string to see if they are equal:

// No cabeçalho:

#define VERDADEIRO 0
#define FALSO -1

// Na sua função:

int ts = strlen(s); // Tamanho da primeira string
int tv = strlen(v): // Tamanho da segunda string
for ( int i = 0 ; i < ts ; i++ ) { // Para cada caractere da primeira string
    char cs = ts[i]; // Esse caractere precisa aparecer em v
    int apareceu = FALSO; // No começo, não apareceu (pois ainda não começamos a buscar)
    for ( int t = 0 ; t < tv ; t++ ) { // Para cada caractere da segunda string
        char cv = tv[t]; // Esse caractere será comparado a cs
        if ( cs == cv ) { // Apareceu!
            apareceu = VERDADEIRO;
            break; // Para de testar a segunda string, pois já achou o caractere
        }
    }
    if ( apareceu == FALSO ) // Se continua FALSO, é porque não apareceu
        return FALSO;
}
return VERDADEIRO; // Se passou por todos os caracteres da primeira string e nenhum
                   // deles retornou FALSO, então é porque todos aparecem na segunda

Obs.: I defined VERDADEIRO as 0 and FALSO as -1, as set out in the question, but this is a rather unusual way of representing these values; one usually uses 0 as false and anything other than 0 as true. Unless you have a good reason to deviate from this convention, I suggest you do not.

  • I think the code @mgibsonbr is very good. Thank you for the comments on each step. It helps a lot :)

  • 2

    +1 for the remark on the C convention

3

Updating

Follow another alternative way without use the function strchr:

int testarString(char *fonte, char encontrar[]){
    int i, ret = 0, tamanho = strlen(encontrar);
    while (*fonte){
        for (i = 0; i < tamanho; i++)
            ret += ((*fonte == encontrar[i]) ? 1: 0);
        *fonte++;
    }
    return ((ret == tamanho) ? 0: 1);
}

Functional example in Ideone.


An alternative that can be used is the function strchr, this function returns the pointer of the first occurrence of a character, it receives two parameters, the first is the string to be covered, and the second, the character to be found.

The idea is to go through the characters of string to be found, in the case of the variable s, and check if there’s a match on string to be checked, in this case, the variable v.

This can be implemented as follows:

int testarString(char fonte[], char encontrar[]){
    int i = 0;
    while (i <= strlen(encontrar)){
        if (strchr(fonte, encontrar[i]) == 0)
            return 1;
        i++;
    }
    return 0;
}

Functional example in Ideone.

2

A more efficient solution to this problem involves sorting the strings before starting to compare them. In pseudo code;

ss = sort(s) // "acb" vira "abc"
sv = sort(v) // "cdeabf" vira "abcdef"

//invariante: Os caracteres de ss[0,i) estão contidos em sv[0,j)
i = j = 0
while i < len(ss) do
    while j < len(sv) and ss[i] != sv[j] do
        j += 1
    done
    if j < len(sv) then
        i += 1  // achei um par para ss[i]
    else
        return NAO // ss[i] não tem par em v
    end
done

return SIM

Most other answers feature quadratic algorithms, which take a time proportional to the size of one string mumltyped by the size of the other. The algorithm I presented consists of a sort step that takes time O(n log n) (or O(n) if you use a life Radix-Sort) followed by a comparison step O(n) that only passes the strings.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.