【问题标题】: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 &lt;= 'z'而不是List
  • 所有alphabets?像希腊语(αβγδε...ψω)和俄语(абвгд...юя)?还是您的意思是“(又名英文)字母表的所有字母”(abcde...yz)?
  • @KevinEsche:如果列表包含不相互跟随的字符,还有 "abc...xyz".toCharArray()
  • 反过来做,循环字母并检查每个字母是否在你的字符串中。

标签: java string arraylist


【解决方案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://stackoverflow.com/a/39263836/1226744) 和@Kevin Esche (https://stackoverflow.com/a/39263917/1226744) 所指出的,您的算法有更好的替代方案

【讨论】:

  • 当您从字符串Character inp = strChar[i]; 获取字符时,提问者可以(并且应该)也使用List&lt;Character&gt; 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 - 好吧,这个性能可以通过使用 PatternMatcher 而不是直接使用 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);

其他一些改进:

  1. 在这种情况下,您应该使用Set 而不是List
  2. 您甚至可以使用boolean[26] 来跟踪是否出现了字母表
  3. 您不需要将字符串转换为 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&lt;&lt;(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&lt;String&gt;。然后您可以简单地调用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 之一。

                【讨论】:

                  猜你喜欢
                  • 2012-02-22
                  • 1970-01-01
                  • 2020-12-12
                  • 2015-01-04
                  • 1970-01-01
                  • 2018-04-18
                  • 1970-01-01
                  相关资源
                  最近更新 更多