2

So far, i have came up with this. I have tried to minimize string operations and isolate solution to built in data types, arrays and integer operations.

I'm in search of much more elegant way to check for a pangram string, in java.

Elegant, as in minimum lines of code, other efficient algorithms are also welcome.

Please provide suggestions without lambda expressions.

    private static boolean isPangrams(String ip) {

        char[] characterArray = ip.toLowerCase().toCharArray();
        int map[] = new int[26];
        int sum = 0;

        for(char current : characterArray) {

            int asciiCode = (int) current;
            if (asciiCode >= 97 && asciiCode <= 122) {

                if (map[122 - asciiCode] == 0) {

                    sum += 1;
                    map[122 - asciiCode] = 1;
                }
            }
        }

        return sum == 26;
    }
Ubik
  • 139
  • 1
  • 6
alkber
  • 1,426
  • 2
  • 20
  • 26
  • 7
    Well, "improving" working code should go to codereview.stackexchange.com ... anyway: it doesn't get much better, but you could be using a Bitset instead of an int array - why use numbers when true/false is what you really need?! – GhostCat Jun 17 '16 at 11:26
  • BitSet , was a neat input. Thanks. – alkber Jun 17 '16 at 13:10

5 Answers5

4

If you want a hard to understand few lines answer:

private static boolean isPangrams(String ip) {
  return 26== (new HashSet(Arrays.asList(ip.toUpperCase().replaceAll("[^A-Z]", "").toCharArray()))).size();
}

Explanation:

  1. make the string uppercase (to handle 'a' and 'A' as the same)
  2. remove all characters not A, B ... Z
  3. convert it to a char[]
  4. convert the array to a Collection
  5. add the collection to a Set to get rid of all doublettes
  6. test the size of the set.

You should realize that this code is not easy to read and not performant.

MrSmith42
  • 9,961
  • 6
  • 38
  • 49
4

You can use bitwise operations for that:

private static boolean isPangrams(String ip) {
    int flags = 0;
    for(char current : ip.toLowerCase().toCharArray()) {
        if (current >= 'a' && current <= 'z') {
            flags |= 0x01<<(current-'a');
        }
    }
    return flags == 0x3ffffff;
}

jDoodle

The code works as follows: we consider an int which is a 32-bit number. Each bit up to 26 is a flag (a boolean so to speak). Initially all flags are false because we initialize flags with 0.

Now we iterate over the characters of the string. In case the character is a lowercase letter, we set the flag of the corresponding flag to true (regardless whether it has been set to true before).

Finally we inspect whether the lowest 26 bits are all set to true. If so, flags is equal to 0x3ffffff (which is a hexadecimal number equal to 1111111111111111111111 binary. If so we return true. Otherwise we return false.

Usually bitwise operations are faster than if statements and booleans so I expect this program to be a significant bit faster.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Why cast the `char` 'current' and store it in an `int` 'asciiCode'? All operations you do can be done with the original `char` 'current' instead of 'asciiCode'. – MrSmith42 Jun 17 '16 at 14:13
2

You can 'pack' a data if a string contains the given letter inside an int variable.

static boolean pangram (String s) {
    int check = 0;
    String lowerCase = s.toLowerCase();
    for (int i = 0; i < lowerCase.length(); i++) {
      char ch = lowerCase.charAt(i);
      if (ch >= 'a' && ch <= 'z') {
        check |= (1 << s.charAt(i) - 'a');
      }
    }
    return check == 67108863;
  }

The magic number in the end is 0b00000011111111111111111111111111

cliffroot
  • 1,839
  • 17
  • 27
0

You could stop the whole method with an return false statement, if you find map[122 - asciiCode] not equals zero, because since then its no pangram anymore and you spare the rest of the for() - am I right? I know this is not the improvement what you expect (especially with only 26 steps) but just something that came in mind to me

        if (map[122 - asciiCode] == 0) {

            sum += 1;
            map[122 - asciiCode] = 1;
        } else return false;
Daddelbob
  • 406
  • 4
  • 13
  • 2
    I thought the definition of pangram was _all_ chars (a-z) _at least_ once. – jensgram Jun 17 '16 at 11:33
  • You are right, a pangram must have every char at least once. I thought of a "perfect pangram" which contains every letter only once. Wikipedia/Pangram: "A perfect pangram contains every letter of the alphabet only once and can be considered an anagram of the alphabet" – Daddelbob Jun 22 '16 at 06:46
0

The most effective solution O(n) time complexity:

  1. Iterate through the string and put each letter into HashMap (key: letter, value: count)
  2. Iterate through the map and check each letter from alphabet
Dmitry Volokh
  • 1,630
  • 16
  • 28