web-dev-qa-db-fra.com

Comment vérifier si une chaîne contient toutes les lettres de l'alphabet?

J'essaie de vérifier si une chaîne contient toutes les lettres de l'alphabet. J'ai créé une ArrayList qui contient tout l'alphabet. J'ai converti la chaîne en tableau de caractères et je parcours le tableau de caractères. Pour chaque caractère présent dans la variable ArrayList, j'en retire un élément. Et à la fin, j'essaie de vérifier si la Arraylist est vide pour voir si tous les éléments ont été supprimés. Cela indiquerait que la chaîne contient toutes les lettres de l'alphabet. 

Malheureusement, le code renvoie une erreur IndexOutOfBoundsException à l'intérieur de la condition if, où je supprime des éléments de l'arraylist.

List<Character> alphabets = new ArrayList<Character>();

alphabets.add('a');
alphabets.add('b');
alphabets.add('c');
alphabets.add('d');
alphabets.add('e');
alphabets.add('f');
alphabets.add('g');
alphabets.add('h');
alphabets.add('i');
alphabets.add('j');
alphabets.add('k');
alphabets.add('l');
alphabets.add('m');
alphabets.add('n');
alphabets.add('o');
alphabets.add('p');
alphabets.add('q');
alphabets.add('r');
alphabets.add('s');
alphabets.add('t');
alphabets.add('u');
alphabets.add('v');
alphabets.add('w');
alphabets.add('x');
alphabets.add('y');
alphabets.add('z');

// This is the string- I've just put a random example
String str = "a dog is running crazily on the ground who doesn't care about the world";

//Remove all the spaces
str = str.replace(" ", "");

// Convert the string to character array
char[] strChar = str.toCharArray();

for (int i = 0; i < strChar.length; i++) {

    char inp = strChar[i];

    if (alphabets.contains(inp)) {
        alphabets.remove(inp);
    }
}

if (alphabets.isEmpty())
    System.out.println("String contains all alphabets");
else
    System.out.println("String DOESN'T contains all alphabets");
13
Siddharth

Toutes ces solutions semblent faire beaucoup de travail pour une vérification relativement simple, en particulier compte tenu de l'API de flux de Java 8:

/* Your lowercase string */.chars()
    .filter(i -> i >= 'a' && i <= 'z').distinct().count() == 26;

Edit: pour la vitesse

Si vous souhaitez mettre fin à l'itération de chaîne dès que l'alphabet entier est trouvé, vous pouvez suivre en interne HashSet:

Set<Integer> chars = new HashSet<>();
String s = /* Your lowercase string */;
s.length > 25 && s.chars()
    .filter(i -> i >= 'a' && i <= 'z') //only alphabet
    .filter(chars::add) //add to our tracking set if we reach this point
    .filter(i -> chars.size() == 26) //filter the 26th letter found
    .findAny().isPresent(); //if the 26th is found, return

De cette façon, le flux cessera dès que la Set sera remplie avec les 26 caractères requis.

Vous trouverez ci-dessous certaines solutions (encore plus efficaces) en termes de performances, mais en guise de remarque personnelle, je vous dirai de ne pas vous perdre dans l'optimisation prématurée, où vous pourriez avoir une lisibilité et moins d'effort pour écrire le code lui-même.

77
Rogue

List.remove supprime par index. Puisqu'un char peut être converti en un entier, vous supprimez effectivement les valeurs d'index inexistantes, c'est-à-dire que le caractère 'a' est égal à int 97. Comme vous pouvez le voir, votre liste ne contient pas 97 entrées.

Vous pouvez faire alphabet.remove(alphabets.indexOf(inp));

Comme l'ont souligné @Scary Wombat ( https://stackoverflow.com/a/39263836/1226744 ) et @Kevin Esche ( https://stackoverflow.com/a/39263917/1226744 ), il existe une meilleure alternative à votre algorithme

16
Leon

Regex est votre ami . Pas besoin d'utiliser une List ici.

public static void main(String[] args) {
    String s = "a dog is running crazily on the ground who doesn't care about the world";
    s = s.replaceAll("[^a-zA-Z]", ""); // replace everything that is not between A-Za-z 
    s = s.toLowerCase();
    s = s.replaceAll("(.)(?=.*\\1)", ""); // replace duplicate characters.
    System.out.println(s);
    System.out.println(s.length()); // 18 : So, Nope

    s = "a dog is running crazily on the ground who doesn't care about the world qwertyuioplkjhgfdsazxcvbnm";
    s = s.replaceAll("[^a-zA-Z]", "");
    s = s.toLowerCase();        
    s = s.replaceAll("(.)(?=.*\\1)", "");
    System.out.println(s);
    System.out.println(s.length()); //26 (check last part added to String)  So, Yes

}
8
TheLostMind

O (n) solution

static Set<Integer> alphabet = new HashSet<>(26);

public static void main(String[] args) {

    int cnt = 0;

    String str = "a dog is running crazily on the ground who doesn't care about the world";

    for (char c : str.toCharArray()) {
        int n = c - 'a';
        if (n >= 0 && n < 26) {
            if (alphabet.add(n)) {
                cnt += 1;
                if (cnt == 26) {
                    System.out.println("found all letters");
                    break;
                }
            }
        }
    }
}
8
hahn

Ajouter à @Leon answer, créer une List et le supprimer semble tout à fait inutile. Vous pouvez simplement boucler sur 'a' - 'z' et faire une vérification avec chaque char. De plus, vous parcourez toute la String pour savoir si chaque lettre est présente. Mais la meilleure version serait de boucler chaque lettre elle-même. Cela peut éventuellement vous protéger en quelques itérations.

À la fin, un exemple simple pourrait ressembler à ceci:

// This is the string- I've just put a random example
String str = "a dog is running crazily on the ground who doesn't care about the world";
str = str.toLowerCase();

boolean success = true;
for(char c = 'a';c <= 'z'; ++c) {
    if(!str.contains(String.valueOf(c))) {
        success = false;
        break;
    }
}

if (success)
    System.out.println("String contains all alphabets");
else
    System.out.println("String DOESN'T contains all alphabets");
6
SomeJavaGuy

Une autre réponse a déjà indiqué le motif de l'exception. Vous avez mal utilisé List.remove(), car il convertit implicitement char en int qu’il a appelé List.remove(int) et qui le supprime par index.

La façon de résoudre est en réalité facile. Vous pouvez le faire appeler le List.remove(Object) par

alphabets.remove((Character) inp);

Quelques autres améliorations:

  1. Vous devez utiliser Set au lieu de List dans ce cas.
  2. Vous pouvez même utiliser un boolean[26] pour savoir si un alphabet est apparu
  3. Vous n'avez pas besoin de convertir votre chaîne en tableau de caractères. Il suffit de faire un str.charAt(index) pour vous donner le personnage à une certaine position.
5
Adrian Shum

Une variable entière suffit pour stocker cette information. Tu peux le faire comme ça

public static boolean check(String input) {
  int result = 0;    
  input = input.toLowerCase();
  for (int i = 0; i < input.length(); i++) {
    char c = input.charAt(i);
    if (c >= 'a' && c <= 'z') {
      result |= 1 << (input.charAt(i) - 'a');
    }
  }
  return result == 0x3ffffff;
}

Chaque bit correspond à une lettre en alphabet anglais. Donc, si votre chaîne contient toutes les lettres, le résultat sera de la forme 00000011111111111111111111111111

4
cliffroot

Que diriez-vous de créer 

List<String> alphabets = new ArrayList <String> ();

et ajouter des valeurs en tant que chaînes

puis

for (String val : alphabets) {   // if str is long this will be more effecient
     if (str.contains (val) == false) {
        System.out.println ("FAIL");
        break;
     }
}
3
Scary Wombat

Vous pouvez vous débarrasser de l'exception en changeant cette ligne dans votre code

char inp = strChar[i];

à

Character inp = strChar[i];

Référez-vous https://docs.Oracle.com/javase/7/docs/api/Java/util/List.html#remove(Java.lang.Object)

List.remove('char') est traité comme List.remove('int'), raison pour laquelle vous obtenez indexOutOfBoundsException, car il vérifie la valeur ASCII de 'a' qui correspond à 97. La conversion de la variable 'inp' en caractère appelle List.remove('Object') api.

3
Arathi Senan

Et si vous aimez les flux Java 8 comme moi:

final List<String> alphabets = new ArrayList<>();

Et après avoir rempli les alphabets avec a-z:

final String str = "a dog is running crazily on the ground who doesn't care about the world";
final String strAsLowercaseAndWithoutOtherChars = str.toLowerCase()
                                                     .replaceAll("[^a-z]", "");

final boolean anyCharNotFound = alphabets.parallelStream()
       .anyMatch(t -> !strAsLowercaseAndWithoutOtherChars.contains(t));

if (anyCharNotFound) {
    System.out.println("String DOESN'T contains all alphabets");
} else {
    System.out.println("String contains all alphabets");
}

Ceci convertit la chaîne en minuscules (ignore si vous ne recherchez que les lettres minuscules), supprime tous les caractères de la chaîne qui ne sont pas des lettres minuscules, puis recherche tous les membres de votre alphabets s'ils sont contenus dans la chaîne à l'aide un flux parallèle.

2
Florian Link

Pour Java 8, cela pourrait être écrit comme suit:

boolean check(final String input) {
    final String lower = input.toLowerCase();
    return IntStream.range('a', 'z'+1).allMatch(a -> lower.indexOf(a) >= 0);
}
1
j2ko

Voici une autre solution naïve qui utilise String.split("") pour scinder chaque caractère dans un tableau String[], puis Arrays.asList() pour le convertir en List<String>. Vous pouvez alors simplement appeler yourStringAsList.containsAll(alphabet) pour déterminer si votre String contient l'alphabet:

String yourString = "the quick brown fox jumps over the lazy dog";

List<String> alphabet = Arrays.asList("abcdefghijklmnopqrstuvwxyz".split(""));
List<String> yourStringAsList = Arrays.asList(yourString.split(""));

boolean containsAllLetters = yourStringAsList.containsAll(alphabet);

System.out.println(containsAllLetters);

Cette approche n'est peut-être pas la plus rapide, mais je pense que le code est un peu plus facile à comprendre que les solutions proposant des boucles et des flux, etc.

1
Kevin Workman

Juste faire quelque chose comme 

sentence.split().uniq().sort() == range('a', 'z')
1
lblzsd
Character inp = strChar[i]; 

Utilisez ceci au lieu de char, la méthode de suppression de liste a 2 méthodes surchargées, une avec objet et une avec int. Si vous transmettez un caractère, il est traité comme un entier. 

0
Rohit

Convertissez la chaîne en minuscule ou en majuscule. Ensuite, parcourez les valeurs décimales ascii équivalentes pour A-Z ou a-z et renvoyez false si elles ne figurent pas dans le tableau de caractères. Vous devrez lancer l'int à char. 

0
Olga

J'ai pensé jouer avec les codes ASCII des personnages. 

String toCheck = yourString.toLowerCase();
int[] arr = new int[26];
for(int i = 0; i < toCheck.length(); i++) {
    int c = ((int) toCheck.charAt(i)) - 97;
    if(c >= 0 && c < 26) 
        arr[c] = arr[c] + 1;
}

Après avoir exécuté la boucle, vous obtenez éventuellement un tableau de compteurs, chacun représentant une lettre d'alphabet (index) et son occurrence dans la chaîne.

boolean containsAlph = true;
for(int i = 0; i < 26; i++)
    if(arr[i] == 0) {
        containsAlph = false;
        break;
    }
0