411. Minimum Unique Word Abbreviation
A string such as "word" contains the following abbreviations:
["word", "1ord", "w1rd", "wo1d", "wor1", "2rd", "w2d", "wo2", "1o1d", "1or1", "w1r1", "1o2", "2r1", "3d", "w3", "4"]
Given a target string and a set of strings in a dictionary, find an abbreviation of this target string with the smallest possible length such that it does not conflict with abbreviations of the strings in the dictionary.
Each number or letter in the abbreviation is considered length = 1. For example, the abbreviation "a32bc" has length = 4.
Note:
- In the case of multiple answers as shown in the second example below, you may return any one of them.
- Assume length of target string = m, and dictionary size = n. You may assume that m ≤ 21, n ≤ 1000, and n + m ≤ 20.
Examples:
"apple", ["blade"] -> "a4" (because "5" or "4e" conflicts with "blade")
"apple", ["plain", "amber", "blade"] -> "1p3" (other valid answers include "ap3", "a3e", "2p2", "3le", "3l1").
Solution: Bit Op
class Solution(object):
def minAbbreviation(self, target, dictionary):
"""
:type target: str
:type dictionary: List[str]
:rtype: str
"""
words = [self.to_number(target, word)
for word in dictionary
if len(word) == len(target)]
if not words:
return str(len(target))
min_len = float('inf')
min_abbr = self.to_number(target, target)
for abbr, length in self.abbrs(min_abbr, len(target)):
if length < min_len and self.valid_abbr(abbr, words):
min_len = length
min_abbr = abbr
return self.to_word(target, min_abbr)
def abbrs(self, word, size, zeros=0):
if word == 0:
yield word, size
else:
for abbr, length in self.abbrs(word >> 1, size, 0):
yield (abbr << 1) + 1, length
if zeros > 0:
size -= 1
for abbr, length in self.abbrs(word >> 1, size, zeros + 1):
yield (abbr << 1) + 0, length
def valid_abbr(self, abbr, words):
return not any(abbr & word == abbr for word in words)
def to_number(self, target, word):
number = 0
for left, right in zip(target, word):
number += left == right
number <<= 1
return number
def to_word(self, target, number):
chars = []
zeros = 0
for char in reversed(target):
if number & 1 == 1:
if zeros > 0:
chars.append(str(zeros))
zeros = 0
chars.append(char)
else:
zeros += 1
number >>= 1
if zeros > 0:
chars.append(str(zeros))
return ''.join(reversed(chars))
Lessons:
- All words in dictionary with different sizes can be ignored. Simply return the size of target.
- Change every word in dictionary to a (binary) number, based on the following rule:
target: apple word: amble number: 10011 - Based on the same rule, we can also change every target's abbreviation into a number. So for a word like
apple, we will have 32 abbreviations, from00000to11111(0 to 31). - To find a valid abbreviation, we need
abbr & word == abbris false for every word in dictionary. False means there is at least one position inabbrthat can match the position in target, whilewordcan not. So1 & 0 = 0which changes theabbrnumber at that position. - Lastly, we can use DFS to find the length of every abbreviation.