Check if a String Contains All Alphabets in Java (Pangram)

Check if a string contains all alphabets in Java with a boolean array, bitmask, streams and regex, list the missing letters and avoid the Unicode traps.

Alphabet table with the letters of Hello, World marked as seen and the other 19 letters marked as missing

A string contains all alphabets when every letter from a to z appears in it at least once, ignoring case, and such a string is called a pangram. In Java, we check it by marking each letter we meet in a boolean[26] array and returning true as soon as all 26 slots are marked.

Pangram checks are a common coding interview puzzle about strings and character arithmetic. Apps use them to validate sample text for font previews and keyboard tests, where every key or glyph must show up.

The following example checks three sentences with the isPangram() method that we write in section 2.

boolean fox = isPangram("The quick brown fox jumps over the lazy dog");   // true
boolean shout = isPangram("PACK MY BOX WITH FIVE DOZEN LIQUOR JUGS");      // true
boolean hello = isPangram("Hello, World");                                 // false

Notice that uppercase letters count, and spaces and punctuation are ignored. We also cover a bitmask version, streams, the regex from older tutorials, a method that lists the missing letters, and the Unicode trap that breaks many online solutions.

1. Rules for a Pangram

A pangram must contain all 26 letters of the English alphabet. The letters can appear in any order and any number of times, so the shortest possible pangram has 26 letters, and the famous fox sentence has 35.

Alphabet table with the letters of Hello, World marked as seen and the other 19 letters marked as missing
Hello, World marks only 7 of the 26 slots, so it is not a pangram

The check follows four rules.

  • Uppercase and lowercase forms of a letter count as the same letter.
  • Digits, spaces, punctuation and emoji are ignored.
  • Repeated letters are allowed, and each letter needs at least one occurrence.
  • Letters outside a to z, such as é or Greek letters, do not count toward the English alphabet.
String fox = "The quick brown fox jumps over the lazy dog";
long letters = fox.chars().filter(Character::isLetter).count();   // 35

2. Checking if a String Contains All Alphabets

The array has one slot per letter, and c – ‘a’ turns a letter into its slot index, so ‘a’ maps to 0 and ‘z’ to 25. A counter tracks how many different letters we have seen, which lets the method stop at the 26th new letter instead of reading the rest of the text.

static boolean isPangram(String text) {
    if (text == null || text.length() < 26) {
        return false;                        // too short to hold 26 letters
    }
    boolean[] seen = new boolean[26];
    int count = 0;
    for (int i = 0; i < text.length(); i++) {
        char c = text.charAt(i);
        if (c >= 'A' && c <= 'Z') {
            c = (char) (c + ('a' - 'A'));    // ASCII upper to lower case
        }
        if (c >= 'a' && c <= 'z' && !seen) {
            seen = true;
            count++;
            if (count == 26) {
                return true;                 // stop early
            }
        }
    }
    return false;
}
boolean nothing = isPangram(null);                                         // false
boolean alphabet = isPangram("abcdefghijklmnopqrstuvwxyz");                 // true
boolean noZ = isPangram("The quick brown fox jumps over the lay dog");      // false

The method converts only the ASCII letters A to Z itself and does not call Character.toLowerCase() or String.toLowerCase(), because both methods turn some non-English characters into English letters. For example, the Kelvin sign (U+212A) becomes a plain k, so a text with the Kelvin sign instead of a k would pass the check. Solutions that compute c – ‘a’ after Character.isLetter() have the opposite problem, because a letter such as é gives an index outside the array.

char kelvinLower = Character.toLowerCase('\u212A');   // k, the Kelvin sign becomes a Latin k
int badIndex = '\u00E9' - 'a';                        // 136, outside the 26 slots

3. Other Ways to Check for All Letters

The boolean array is the version most interviewers expect. The same idea fits in a single int, in a stream, and in a regular expression, and each version has its own trade-off.

3.1. Using a Bitmask

An int has 32 bits, so bits 0 to 25 can stand for the letters a to z. The |= operator sets the bit for each letter, and the text is a pangram when all 26 low bits are set, which is the value (1 << 26) – 1.

static boolean isPangramBits(String text) {
    final int all = (1 << 26) - 1;
    int seen = 0;
    for (int i = 0; i < text.length() && seen != all; i++) {
        int c = text.charAt(i) | 0x20;       // lower case for ASCII letters
        if (c >= 'a' && c <= 'z') {
            seen |= 1 << (c - 'a');
        }
    }
    return seen == all;
}
boolean bitsFox = isPangramBits("Sphinx of black quartz, judge my vow");   // true
int allBits = (1 << 26) - 1;                                                 // 67108863

The trick | 0x20 sets the bit that separates ASCII upper and lower case, so ‘A’ (65) becomes ‘a’ (97). A character outside the ASCII letters never lands in the range a to z after the OR, so the range check rejects it.

3.2. Using Streams

A stream version filters the ASCII letters, maps them to lower case, removes duplicates with distinct() and counts what is left. It is the shortest code, but it always reads the whole string and boxes each distinct value.

static boolean isPangramStream(String text) {
    return text.chars()
            .filter(c -> (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
            .map(Character::toLowerCase)
            .distinct()
            .count() == 26;
}
boolean streamFox = isPangramStream("Jived fox nymph grabs quick waltz");   // true

3.3. Using indexOf() for Each Letter

The loop from older tutorials asks the string for each of the 26 letters in both cases. It reads the string up to 52 times, but the code is short and needs no array.

static boolean isPangramIndexOf(String text) {
    for (char c = 'a'; c <= 'z'; c++) {
        if (text.indexOf(c) < 0 && text.indexOf(Character.toUpperCase(c)) < 0) {
            return false;                    // c is missing
        }
    }
    return true;
}
boolean indexFox = isPangramIndexOf("The five boxing wizards jump quickly");   // true

3.4. Using a Regex with Lookaheads

A regex can require each letter with a lookahead such as (?=.*a), which matches when an a appears anywhere after the current position. We build the 26 lookaheads in a loop and compile the Pattern once, for example into a static final field.

static Pattern pangramPattern() {
    String lookaheads = IntStream.rangeClosed('a', 'z')
            .mapToObj(c -> "(?=.*" + (char) c + ")")
            .collect(Collectors.joining());
    return Pattern.compile("(?is)" + lookaheads + ".*");
}
Pattern pangram = pangramPattern();
boolean regexFox = pangram.matcher("The quick brown fox\njumps over the lazy dog").matches();   // true

The s flag (DOTALL) matters. Without it, . does not match a line break, so a pangram that spans two lines fails the check, which is a bug in many regex solutions. Each lookahead scans the text again, so the regex is the slowest option and best kept for validation rules that must be a pattern, such as a form field.

boolean oneLine = "The quick brown fox\njumps over the lazy dog".matches("(?i)(?=.*a)(?=.*z).*");   // false, no DOTALL

4. Listing the Missing Letters

A false result does not say what is wrong. When a user types the text, a list of the missing letters is more useful, so the method fills the same array and collects the empty slots.

static String missingLetters(String text) {
    boolean[] seen = new boolean[26];
    for (char c : text.toCharArray()) {
        int lower = c | 0x20;
        if (lower >= 'a' && lower <= 'z') {
            seen[lower - 'a'] = true;
        }
    }
    StringBuilder missing = new StringBuilder();
    for (int i = 0; i < 26; i++) {
        if (!seen[i]) {
            missing.append((char) ('a' + i));
        }
    }
    return missing.toString();
}
String gaps = missingLetters("Hello, World");                           // "abcfgijkmnpqstuvxyz"
String noGaps = missingLetters("Pack my box with five dozen liquor jugs");  // ""

For example, a font preview page in a design tool lets users enter their own sample text. Before saving it, the page calls missingLetters() and shows a hint such as “Add the letters q and z”, so every glyph of the font appears in the preview.

5. Time and Space Complexity

All the versions read the text in O(n) time, where n is the string length, and the alphabet size is a constant. The differences are the number of passes, the early exit and the memory, so the table compares them for a text of n characters.

ApproachPasses over the textStops earlyExtra memory
boolean[26] with a counter1Yes26 booleans
Bitmask in an int1YesOne int
Stream with distinct()1NoA set of up to 26 values
indexOf() per letterUp to 52Yes, on the first missing letterNone
Regex lookaheadsUp to 26Yes, on the first failed lookaheadPattern and matcher state

For one sentence, any version is fast. The choice matters when a service checks many long texts, where the single-pass array or bitmask versions do the least work.

6. Pangram FAQs

Readers who solve the puzzle often ask about famous pangrams, other alphabets and stricter variants.

6.1. Is “The quick brown fox jumps over the lazy dog” a pangram?

Yes. It contains all 26 letters in 35 letters total, and typists have used it for decades to test keyboards and typewriters.

6.2. What is a perfect pangram?

A perfect pangram uses each letter only once, so it has 26 letters and no more. We check it with isPangram() plus a count of the letters, which must be 26.

String word = "Mr Jock, TV quiz PhD, bags few lynx";
boolean perfect = isPangram(word) && word.chars().filter(Character::isLetter).count() == 26;   // true

6.3. How do we check a pangram for another alphabet?

We replace the 26 slots with a Set of the expected letters for that language and remove each letter we find from a copy of the set. The text is a pangram when the copy is empty. For German, the set also holds ä, ö, ü and ß.

6.4. Should the check ignore case?

Yes, in almost every version of the puzzle, because A and a are the same letter. If the task asks for lowercase input only, we can drop the conversion, but the case-insensitive code still works on lowercase text.

7. Conclusion

A string is a pangram when each letter from a to z appears at least once in any case. The boolean[26] array with a counter reads the text once, stops at the 26th new letter and needs no library, so it makes a good interview answer.

The bitmask fits the same idea into one int, and streams and regex trade speed for shorter code. Whatever the version, convert only the ASCII letters to lower case, and add the DOTALL flag when a regex must work across lines.

8. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. Here is a hardcore way! =)
    O(N) time and O(N) space:

    boolean check1(String str) {
    int bits = 0;
    for (char c : str.toCharArray()) {
    bits |= 1 << c – 'a';
    if (bits == (1 < bits[0] |= 1 < bits[0] == (1 << 'z' – 'a' + 1) – 1);
    }

    • Looks like part of my comment was missed, here is even better solution that avoids copying array and so we have O(N) time and O(1) space, but it does bad thing in terms of clear code – changes external state during stream processing

      boolean check2(String str) {
      int[] bits = {0};
      return str.chars()
      .map(c -> bits[0] |= 1 < bits[0] == (1 << 'z' – 'a' + 1) – 1);
      }

  2. Your offered solutions are both O(N^2) and O(1) space. By using a HashMap, you can get O(N) performance and O(N) space

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.