0

Im trying to generate a word with a wild card and check and see if this word is stored in the dictionary database. Like "appl*" should return apply or apple. However the problem comes in when I have 2 wild cards. "app**" will make words like appaa, appbb..appzz... instead of apple. The second if condition is just for a regular string that contains no wildcards"*"

 public static boolean printWords(String s) {
    String tempString, tempChar;
    if (s.contains("*")) {

        for (char c = 'a'; c <= 'z'; c++) {

            tempChar = Character.toString(c);
            tempString = s.replace("*", tempChar);

            if (myDictionary.containsKey(tempString) == true) {
                System.out.println(tempString);
            } 
        } 
    }
    if (myDictionary.containsKey(s) == true) {
        System.out.println(s);
        return true;
    } else {
        return false;
    }
}
user3072188
  • 305
  • 1
  • 3
  • 6

3 Answers3

1

You're only using a single for loop over characters, and replacing all instances of * with that character. See the API for String.replace here. So it's no surprise that you're getting strings like Appaa, Appbb, etc.

If you want to actually use Regex expressions, then you shouldn't be doing any String.replace or contains, etc. etc. See Anubian's answer for how to handle your problem.

If you're treating this as a String exercise and don't want to use regular expressions, the easiest way to do what you're actually trying to do (try all combinations of letters for each wildcard) is to do it recursively. If there are no wild cards left in the string, check if it is a word and if so print. If there are wild cards, try each replacement of that wildcard with a character, and recursively call the function on the created string.

public static void printWords(String s){
    int firstAsterisk = s.indexOf("*");
    if(firstAsterisk == -1){ // doesn't contain asterisk
        if (myDictionary.containsKey(s))
            System.out.println(s);
        return;
    }

    for(char c = 'a', c <= 'z', c++){
        String s2 = s.subString(0, firstAsterisk) + c + s.subString(firstAsterisk + 1);
        printWords(s2);
    }
}

The base cause relies on the indexOf function - when indexOf returns -1, it means that the given substring (in our case "*") does not occur in the string - thus there are no more wild cards to replace.

The substring part basically recreates the original string with the first asterisk replaced with a character. So supposing that s = "abcd**ef" and c='z', we know that firstAsterisk = 4 (Strings are 0-indexed, index 4 has the first "*"). Thus,

String s2 = s.subString(0, firstAsterisk) + c + s.subString(firstAsterisk + 1);
          = "abcd" + 'z' + "*ef"
          = "abcdz*ef"
Mshnik
  • 7,032
  • 1
  • 25
  • 38
  • +1 Great answer! If you explained a bit of how your code worked, it'd be an even better answer. Particularly the recursion part, it's not intuitive for everyone. – Anubian Noob Apr 23 '15 at 23:13
  • Thanks. I understand the recursive part. I'm a little confused on the substring part. Also can you explain the base case -1 part please? – user3072188 Apr 24 '15 at 06:19
0

The * character is a regex wildcard, so you can treat the input string as a regular expression:

for (String word : myDictionary) {
    if (word.matches(s)) {
        System.out.println(word);
    }
}

Let the libraries do the heavy lifting for you ;)

Anubian Noob
  • 13,426
  • 6
  • 53
  • 75
  • So with this I'd have to iterate over the whole word list? Is there a way to not have to do that? – user3072188 Apr 24 '15 at 06:21
  • True... I don't think so, so I guess this is slower...? Depends on the size of the word and size of the dictionary, but likely slower. – Anubian Noob Apr 24 '15 at 14:17
0

With your approach you have to check all possible combinations. The better way would be to make a regex out of your input string, so replace all * with ..

Than you can loop over your myDirectory and check for every entry whether it matches the regex.

Something like this:

Set<String> dict = new HashSet<String>();
dict.add("apple");

String word = "app**";
Pattern pattern = Pattern.compile(word.replace('*', '.'));

for (String entry : dict) {
  if (pattern.matcher(entry).matches()) {
    System.out.println("matches: " + entry);
  }
}

You have to take care if your input string already contains . than you have to escape them with a \. (The same for other special regex characters.)

See also http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html and http://docs.oracle.com/javase/6/docs/api/java/util/regex/Matcher.html

tomse
  • 501
  • 2
  • 7