Как проверить, содержит ли строка все буквы алфавита?
Я пытаюсь проверить, содержит ли строка все буквы алфавита. Я создал 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");
Ответы
Ответ 1
Кажется, что все эти решения проделывают большую работу для относительно простой проверки, особенно с учетом потокового API Java 8:
/* Your lowercase string */.chars()
.filter(i -> i >= 'a' && i <= 'z')
.distinct().count() == 26;
Изменение: для скорости
Если вы хотите завершить итерацию строки, как только будет найден весь алфавит, все еще используя потоки, вы можете отслеживать 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
List.remove
удаляется по индексу. Поскольку a char
может быть применено к int, вы эффективно удаляете индексы, которые не существуют, т.е. char 'a' равно int 97. Как вы видите, ваш список не содержит 97 записей.
Вы можете сделать alphabet.remove(alphabets.indexOf(inp))
;
Как указано @Scary Wombat (fooobar.com/questions/418762/...) и @Kevin Esche (fooobar.com/questions/418762/...), есть более эффективная альтернатива вашему алгоритму
Ответ 3
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
}
Ответ 4
O (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;
}
}
}
}
}
Ответ 5
Добавление в ответ @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");
Ответ 6
Еще один ответ уже указал причину исключения. Вы неправильно использовали List.remove()
, поскольку он неявно преобразовывал char
в int
, который он назвал List.remove(int)
, который удаляет по индексу.
Способы решения на самом деле легки. Вы можете заставить его вызвать List.remove(Object)
на
alphabets.remove((Character) inp);
Некоторые другие улучшения:
- В этом случае вы должны использовать
Set
вместо List
.
- Вы даже можете использовать
boolean[26]
для отслеживания появления алфавита.
- Вам не нужно преобразовывать строку в массив char. Просто выполните
str.charAt(index)
, чтобы дать вам символ в определенной позиции.
Ответ 7
Для сохранения этой информации достаточно одной целочисленной переменной. Вы можете сделать это так:
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
Ответ 8
Как создать
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;
}
}
Ответ 9
Вы можете избавиться от исключения, изменив эту строку в своем коде
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. Преобразование переменной 'inp' в Character вызывало бы List.remove('Object')
апи.
Ответ 10
И если вам нравятся потоки 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
, если они содержатся в строке с помощью параллельного потока.
Ответ 11
Для Java 8 его можно записать так:
boolean check(final String input) {
final String lower = input.toLowerCase();
return IntStream.range('a', 'z'+1).allMatch(a -> lower.indexOf(a) >= 0);
}
Ответ 12
Просто сделайте что-нибудь вроде
sentence.split().uniq().sort() == range('a', 'z')
Ответ 13
Здесь другое наивное решение, которое использует 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);
Этот подход может быть не самым быстрым, но я считаю, что код легче понять, чем решения, предлагающие циклы и потоки и многое другое.
Ответ 14
Character inp = strChar[i];
Используйте это вместо char
, метод удаления списка имеет 2 перегруженных метода, один с объектом и один с int. Если вы проходите char, его обрабатывают как int.
Ответ 15
Преобразуйте строку в нижний регистр или в капители. Затем выполните петлю через эквивалентные десятичные значения ascii для A-Z или a-z и верните false, если они не найдены в массиве символов. Вам нужно будет ввести int в char.
Ответ 16
Я думал об игре с кодами символов 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;
}