2016-09-01 7 views
13

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

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

Вместо того, чтобы делать «Список», вы могли бы просто зациклиться как 'int i = (int) 'a'; i <= 'z'' – SomeJavaGuy

+11

Все [алфавиты] (https://en.wikipedia.org/wiki/List_of_writing_systems#True_alphabets)? Как греческий (αβγδε ... ψω) и русский (абвгд ... юя)? Или вы имели в виду «все буквы» (ака английского) алфавита »(abcde ... yz)? – Andreas

+0

Связанные: [Эффективные конструкции языка Java, чтобы проверить, является ли строка pangram?] (Http://stackoverflow.com/q/37880301/2513200) – Hulk

ответ

77

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

/* 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 обязательными символами.

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

+2

+1 элегантное решение для английского языка. Но фильтр быстро становится намного сложнее для других языков (как и определения того, что означает «pangram» - как обрабатывать акцентированные версии букв и т. Д.) – Hulk

+1

Правда, однако это довольно немного изменчиво, поскольку вы можете заменить различные фильтры и размеры алфавита. Вы можете использовать перечисление для этого, даже. :) – Rogue

+0

Я все еще новичок в Java 8, и поэтому у меня есть вопрос. Что такое BigO для этого метода цепочки решения? – hahn

16

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), есть лучшая альтернатива вашему алгоритму

+2

Ответчик мог (и должен) также использовать «Список list' и использовать поле« Символ », когда вы получаете символ из строки« Символ inp = strChar [i]; »С этим он избегает вызова неправильного' remove 'метод тоже. –

+0

Спасибо за это. Я использовал Hashset вместо ArrayList, и он тоже работал. – Siddharth

3

Как о создании

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

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

Способ решения на самом деле прост. Вы можете сделать это назвать List.remove(Object) по

alphabets.remove((Character) inp); 

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

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

Используйте это вместо char, списка удалить метод есть 2 перегруженные методы, один с объектом и один с междунар .Если вы передаете символ его рассматривали как INT один.

8

Regex является вашим другом. Не нужно использовать здесь 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 

} 
+0

Это классный ответ, но на самом деле он не отвечает, почему код рушился. –

+1

@PierreArlaud - Вы правы. Он не отвечает, почему код OP сбой.Но, отвечая на вопрос, попросив OP использовать другой подход, прекрасно (потому что в будущем другие люди могли бы приехать сюда и могли бы быть благодарны за все «новые подходы») – TheLostMind

+0

Поскольку мы на нем, вы думаете, это решение будет - по производительности - обойти подходы с использованием списков? –

6

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

В конце концов простой пример может выглядеть следующим образом:

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

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

0

Я думал об игре с кодами символов 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; 
    } 
+0

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

2

И если вы, как Java 8 потоков, как я:

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

И после заполнения алфавитов с аз:

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, если они содержатся в строке, используя параллельный поток.

3

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

char inp = strChar[i]; 

в

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. Convertin g variable 'inp' to Character будет вызывать List.remove('Object') api.

8

О (п) раствор

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

Это * * ответ. Когда я смотрю на другие ответы, я слышу голос в моей голове, кричащий «ЭЙ! СМОТРИТЕ С КОМПЛЕКСНОСТЬЮ!» –

+0

Переведите проверку счетчика в цикл for, чтобы он спустился сразу после того, как он найдет все буквы, вместо того, чтобы безо всякой проверки всей строки. – iobender

4

Один целой переменной достаточно, чтобы хранить эту информацию. Вы можете сделать это так:

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

+1

1. Нужно ли ** без ** создавать новый массив с 'toCharArray' (' toLowerCase' является необязательным и может быть легко прокомментирован, когда это не требуется), 2. Не ** использует ** нечетные RegExes (I означает, Regex, для этого, серьезно?), 3. Не делает ** не ** делать какие-либо сложности с увеличением сложности с помощью «indexOf» и т. д., 4. Не ** ли ** без необходимости хранить символы (в коробке!) в сборнике. Он ближе всего к решению, которое я использовал бы - и, следовательно, единственный ответ здесь, который получает +1 от меня (с '1 << (c-'a ')' и ранним возвратом 'if (result == 0x3ffffff) return true' ** in ** цикл все равно будет лучше) – Marco13

1

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

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

Просто сделать что-то вроде

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

@Laurel никогда не говорил, что это тарабарщина. Но оскорбления не имеют места в ответе. Либо ответьте красиво, либо двигайтесь вперед. –

1

Вот еще наивным решение который использует 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); 

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

Смежные вопросы