For each word, we basically store all versions of it after removal of 1 character. For example,

1
2
3
4
5
6
hello ->
ello -> removed h@0
hllo -> removed e@1
helo -> removed l@2
helo -> removed l@3
hell -> removed o@4

We can store: wordAfterRemoval,indexOfRemoval in hashmap. So whenever we search for a word like: hexlo then we can try removing it’s 2nd index and search: helo,2 in the map, which we will find.

In the value we can store the removed character. For example, store key=helo,2 with value=l to indicate that l was removed.

So when you match a word like hexlo. Try to search helo,2 in the hashmap. It gives value=l which is !='x' i.e. the removed character in hexlo.

However, there is one problem with this approach. When you add 2 words like: hello and hallo in the map. Then hllo,1 will give e in the first insertion and a in the second. The second overides the first so hello is forgetten by the structure. To avoid this issue, we store both of them (e, a) in a list.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class MagicDictionary {
    private map = new Map<string,string[]>();

    buildDict(dictionary: string[]): void {
        for ( const word of dictionary ) {
            for ( let i=0; i < word.length; i++ ) {
                
                const wordAfterRemoval = word.slice(0, i) + word.slice(i+1);
                // key stores wordAfterRemoval,indexOfRemoval as value
                const key = `${wordAfterRemoval},${i}`;

                if ( !this.map.has(key) )
                    this.map.set(key,[]);

                // values have the removed character
                this.map.get(key).push( word[i] )
            }
        }
    }
    search(word: string): boolean {
        for ( let i=0; i < word.length; i++ ) {
            const wordAfterRemoval = word.slice(0, i) + word.slice(i+1);
            const key = `${wordAfterRemoval},${i}`;
            if ( !this.map.has(key) ) continue;
            const removedChars = this.map.get(key);
            
            // If there was a word with removed character != word[i]
            for ( let j=0; j < removedChars.length; j++ ) {
                if ( removedChars[j] !== word[i] )
                    return true;
            }
        }

        return false;
    }
}

You can also embed the index information by replacing the removed word with _ like in this pyton version:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class MagicDictionary:

    def __init__(self):
        self.map = {}

    def buildDict(self, dictionary: List[str]) -> None:
        for word in dictionary:
            for i in range(len(word)):
                key = word[0:i] + '_' + word[i+1:]
                
                if not key in self.map:
                    self.map[key] = []
                
                self.map[key].append(word[i])
        
    def search(self, searchWord: str) -> bool:
        for i in range(len(searchWord)):
            key = searchWord[0:i] + '_' + searchWord[i+1:]
            
            if key not in self.map: 
                continue
            
            for c in self.map[key]:
                if c != searchWord[i]:
                    return True
        
        return False

# Your MagicDictionary object will be instantiated and called as such:
# obj = MagicDictionary()
# obj.buildDict(dictionary)
# param_2 = obj.search(searchWord)