Как проверить, содержит ли строка все буквы алфавита? [дубликат]

этот вопрос уже есть ответ здесь:

Я пытаюсь проверить, если строка содержит все буквы алфавита. Я создал ArrayList который содержит весь алфавит. Я преобразовал строку в массив char, и я повторяю массив символов и для каждого символа, присутствующего в ArrayList Я удаляю из него элемент. И в конце концов, я пытаюсь проверить, если Arraylist пуст, чтобы увидеть, были ли удалены все элементы. Это означает, что строка содержит все буквы алфавита.

к сожалению, код бросает IndexOutOfBoundsException ошибка внутри условия if, где я удаляю элементы из 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");

16 ответов


все эти решения, похоже, делают много работы для относительно простой проверки, особенно учитывая API потока Java 8:

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

Edit: для скорости

если вы хотите завершить итерацию строки, как только будет найден весь алфавит, вы можете отслеживать с помощью 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

таким образом, поток прекратится, как только Set заполняется 26 символы.

есть некоторые (даже еще) больше эффективные решения с точки зрения производительности ниже, но в качестве личного замечания я скажу, чтобы не болтаться в преждевременной оптимизации слишком много, где вы могли бы иметь читаемость и меньше усилий при написании фактического кода.


List.remove удаляет по индексу. Сchar можно привести к int вы эффективно удаляете значения Индекса, которые не существуют, т. е. char 'a' равен int 97. Как вы можете видеть, в вашем списке нет 97 записей.

можно сделать alphabet.remove(alphabets.indexOf(inp));

как указано @Scary Wombat(https://stackoverflow.com/a/39263836/1226744) и @Kevin Esche (https://stackoverflow.com/a/39263917/1226744), лучшая альтернатива к вашему алгоритм


выражение - твой друг. Нет необходимости использовать List здесь.

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

}

о(n)

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

добавление в @Leon answer, создание List и вынимать из него, кажется совершенно ненужным. Вы можете просто зациклиться 'a' - 'z' и сделайте проверку с каждым char. Кроме того, вы зацикливаетесь на всем String чтобы узнать, присутствует ли каждая буква. Но лучшим вариантом было бы зацикливаться на каждой букве. Это может potentionally безопасный вам несколько итераций.

в конце простой пример может выглядеть так:

// 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");

другой ответ уже указал на причину исключения. Вы используете List.remove(), так как он неявно преобразовать char to int, который называется List.remove(int) которые удалить по индексу.

путь к решению на самом деле прост. Вы можете заставить его вызвать List.remove(Object) by

alphabets.remove((Character) inp);

некоторые другие улучшения:

  1. вы должны использовать Set вместо List в этом случае.
  2. вы даже можете использовать boolean[26] отслеживать появился ли алфавит
  3. вам не нужно преобразовывать строку в массив символов. Просто сделайте str.charAt(index) даст вам характер в определенном положении.

для хранения этой информации достаточно одной целочисленной переменной. Вы можете сделать это

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

каждый бит соответствует букве в английском алфавите. Поэтому, если ваша строка содержит все буквы, результат будет иметь форму 00000011111111111111111111111111


Как насчет создания

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

и добавьте значения в виде строк

затем

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

вы можете избавиться от исключения, изменив эту строку в своем коде

char inp = strChar[i];

to

Character inp = strChar[i];

см.https://docs.oracle.com/javase/7/docs/api/java/util/List.html#remove(java.lang.Object)

List.remove('char') трактуется как List.remove('int'), поэтому вы получаете indexOutOfBoundsException, потому что он проверяет ASCII значение 'a', которое составляет 97. Преобразование переменной " inp " в символ вызовет List.remove('Object') API-интерфейс.


и если вам нравится Java 8 потоков, как я:

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

и после заполнения алфавитов с 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");
}

это преобразует строку в нижний регистр (пропустите, если вы действительно ищете только маленькие буквы), удаляет все символы из строки, которые не являются маленькими буквами, а затем проверяет все члены вашего alphabets если они содержатся в строку с помощью параллельного потока.


вот еще одно наивное решение, которое использует String.split("") разбить каждый символ на String[] массив, а затем Arrays.asList() чтобы преобразовать это в List<String>. Затем вы можете просто позвонить yourStringAsList.containsAll(alphabet) чтобы определить, является ли String содержит алфавит:

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

этот подход может быть не самым быстрым, но я думаю, что код немного легче понять, чем решения, предлагающие циклы, потоки и тому подобное.


просто сделайте что-то вроде

sentence.split().uniq().sort() == range('a', 'z')

для Java 8, это может быть записано так:

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

преобразуйте строку в нижний регистр или заглавные буквы. Затем цикл через эквивалентные десятичные значения ascii для A-Z или a-z и возвращает false, если не найден в массиве символов. Вам придется бросить int в char.


Я думал об игре с ASCII-коды символов.

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

после выполнения цикла вы в конечном итоге получите массив счетчиков, каждый из которых представляет букву алфавита (индекс) и это вхождение в строке.

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

Character inp = strChar[i]; 

используйте это вместо char, метод удаления списка имеет 2 перегруженных метода, один с object и один с int .Если вы передадите char, его будут рассматривать как int one.