如何检查字符串是不是包含字母表中的所有字母? [复制]
Posted
技术标签:
【中文标题】如何检查字符串是不是包含字母表中的所有字母? [复制]【英文标题】:How to check if a string contains all the letters of the alphabet? [duplicate]如何检查字符串是否包含字母表中的所有字母? [复制] 【发布时间】:2017-01-08 21:05:38 【问题描述】:我正在尝试检查一个字符串是否包含字母表中的所有字母。我创建了一个包含整个字母表的ArrayList
。我将字符串转换为 char 数组,并遍历字符数组,并且对于 ArrayList
中存在的每个字符,我从中删除一个元素。最后,我试图检查Arraylist
是否为空,以查看是否所有元素都已删除。这将表明该字符串包含字母表中的所有字母。
不幸的是,代码在我从数组列表中删除元素的 if 条件内抛出 IndexOutOfBoundsException
错误
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");
【问题讨论】:
你可以循环为int i = (int)'a'; i <= 'z'
而不是List
所有alphabets?像希腊语(αβγδε...ψω)和俄语(абвгд...юя)?还是您的意思是“(又名英文)字母表的所有字母”(abcde...yz)?
相关:Efficient Java language constructs to check if string is pangram?
@KevinEsche:如果列表包含不相互跟随的字符,还有 "abc...xyz".toCharArray()
反过来做,循环字母并检查每个字母是否在你的字符串中。
【参考方案1】:
所有这些解决方案似乎都为相对简单的检查做了很多工作,特别是考虑到 Java 8 的流 API:
/* 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 个字符,流就会停止。
在下面的性能方面有一些(甚至仍然)更有效的解决方案,但作为个人说明,我会说不要过多地陷入过早的优化中,这样您可以在编写实际代码时获得可读性和更少的努力.
【讨论】:
+1 优雅的英语解决方案。但是对于其他语言,过滤器很快就会变得复杂得多(“pangram”的定义也是如此——如何处理字母的重音版本等) 是的,但是这有点可变,因为您可以替换不同的过滤器和字母大小。您甚至可以为此使用枚举。 :) 这个问题是长字符串可能需要很长时间 - 它会处理整个字符串,即使字符串的一小部分包含所有字母。考虑一个字符串,其前 26 个字母是字母表,然后是 100 万个其他字符。一个聪明的解决方案是在第一个 26 个字符之后停止搜索。 一些 JMH 测试将 vanilla java(来自 @hahn 的实现)与上面的流实现(AVGT,CNT 200)进行比较:流:1.118 ± 0.012 ns/op;原版:0.470 ± 0.009 ns/op @iobender 更新了我的答案,我想这有点混乱,但至少有速度优势【参考方案2】:List.remove
按索引删除。由于 char
可以转换为 int,因此您实际上删除了不存在的索引值,即 char 'a' 等于 int 97。如您所见,您的列表没有 97 个条目。
你可以alphabet.remove(alphabets.indexOf(inp))
;
正如@Scary Wombat(https://***.com/a/39263836/1226744) 和@Kevin Esche (https://***.com/a/39263917/1226744) 所指出的,您的算法有更好的替代方案
【讨论】:
当您从字符串Character inp = strChar[i];
获取字符时,提问者可以(并且应该)也使用List<Character> list
并使用盒装Character
这样他就避免调用错误的remove
方法,也是。
谢谢。我使用 Hashset 而不是 ArrayList,它也有效。【参考方案3】:
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;
【讨论】:
这就是 的答案。当我查看其他答案时,我听到脑海中的声音在尖叫“嘿!看看复杂性!” 将计数检查移到 for 循环中,这样它会在找到所有字母后立即退出,而不是不必要地检查整个字符串。【参考方案4】:添加到@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");
【讨论】:
简洁优雅!【参考方案5】:正则表达式是你的朋友。此处无需使用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
【讨论】:
这是一个很酷的答案,但它实际上并没有回答代码崩溃的原因 @PierreArlaud - 你是对的。它没有回答为什么 OP 的代码会崩溃。但是,通过要求 OP 使用不同的方法来回答问题是可以的(因为将来其他人可能会来到这里并且可能会感谢所有“新方法”) 既然我们已经完成了,您认为这个解决方案在性能方面会超越使用列表的方法吗? @PierreArlaud - 好吧,这个性能可以通过使用Pattern
和 Matcher
而不是直接使用 String#replaceAll()
来提高。但是不,这个答案不会比List
/ Map
方法快,其中复杂度是O(n)
如果 OP 代码在尝试自己调试后仍然崩溃,这意味着 OP 对其代码中的语句有误解。由于所有这些都是非常通用的(它不是未知的第三方 API 崩溃),如果 OP 不了解他的代码崩溃的原因,他很可能会再次遇到同样的问题。【参考方案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
【讨论】:
这 1. not 是否不必要地使用toCharArray
创建一个新数组(toLowerCase
是可选的,不需要时可以很容易地注释掉),2. 是否不 使用奇怪的正则表达式(我的意思是,正则表达式,是认真的吗?),3. 不 是否对indexOf
等进行任何增加 O 复杂度的操作, 4. 不是不必要地将(装箱的!)字符存储在集合中。它最接近我将使用的解决方案 - 因此,这里唯一得到我 +1 的答案(1<<(c-'a')
和早期返回 if (result==0x3ffffff)return true
in 循环它会还是更好)【参考方案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 的原因,因为它正在检查 'a' 的ASCII
值,即 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】:这是另一个简单的解决方案,它使用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);
这种方法可能不是最快的,但我认为代码比提出循环和流之类的解决方案更容易理解。
【讨论】:
【参考方案12】:做一些类似的事情
sentence.split().uniq().sort() == range('a', 'z')
【讨论】:
@Laurel 从未说过这是胡言乱语。但是侮辱在答案中没有立足之地。要么好好回答,要么继续前进。【参考方案13】:对于 Java 8,可以这样写:
boolean check(final String input)
final String lower = input.toLowerCase();
return IntStream.range('a', 'z'+1).allMatch(a -> lower.indexOf(a) >= 0);
【讨论】:
【参考方案14】:将字符串转换为小写或大写。然后遍历 A-Z 或 a-z 的等效 ascii 十进制值,如果在字符数组中找不到,则返回 false。您必须将 int 转换为 char。
【讨论】:
【参考方案15】:我考虑过使用字符的 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;
【讨论】:
这可能会导致数组的负索引,所以显然我应该编辑它以关心非拉丁字母字符。【参考方案16】:Character inp = strChar[i];
使用 this 而不是 char
,List remove 方法有 2 个重载方法,一个带有 object,一个带有 int。如果您传递 char,则它被视为 int 之一。
【讨论】:
以上是关于如何检查字符串是不是包含字母表中的所有字母? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
如何检查字符串是不是至少包含一个数字、字母和既不是数字也不是字母的字符?