LeetCode全集请参考:LeetCode Github 大全
题目
Given a pattern and a string s, find if s follows the same pattern.
Here follow means a full match, such that there is a bijection between a letter in pattern and a non-empty word in s.
Example 1:
Input: pattern = "abba", s = "dog cat cat dog"
Output: true
Example 2:
Input: pattern = "abba", s = "dog cat cat fish"
Output: false
Example 3:
Input: pattern = "aaaa", s = "dog cat cat dog"
Output: false
Example 4:
Input: pattern = "abba", s = "dog dog dog dog"
Output: false
Constraints:
- 1 <= pattern.length <= 300
- pattern contains only lower-case English letters.
- 1 <= s.length <= 3000
- s contains only lower-case English letters and spaces ’ '.
- s does not contain any leading or trailing spaces.
- All the words in s are separated by a single space.
方法一
方法1:两个哈希图
错误的直觉:
开始思考此问题的最幼稚的方法是拥有一个哈希表,跟踪哪个字符(pattern)映射到哪个单词(s)。当您扫描每个字符-单词对时,请更新此哈希映射以查找映射中未包含的字符。如果看到已经是映射键之一的字符,请检查当前单词是否与该字符映射到的单词匹配。如果它们不匹配,则可以立即返回False,否则,请继续扫描直到结束。
这种检查对于以下情况将非常有效:
- “ abba”和“ dog cat cat dog”->返回True。
- “ abba”和“ dog cat cat fish”->返回False。
但这将失败:
- “ abba”和“ dog dog dog dog”->返回True(预期False)。
一个解决方法是有两个哈希映射,一个用于将字符映射到单词,另一个用于将单词映射到字符。在扫描每个字符-单词对时,
- 如果字符不在字符到单词的映射中,则还要检查该单词是否也在单词到字符的映射中。
如果该单词已存在于单词到字符的映射中,则您可以False立即返回,因为它之前已与其他字符映射过。
否则,更新两个映射。 - 如果字符到单词映射中的字符为IN,则只需要检查当前单词是否与字符到单词映射中该字符映射到的单词匹配。如果没有,您可以False立即返回。
public boolean wordPatternWithTwoMap(String pattern, String s) {
// check edge
if (pattern == null || s == null) {
return pattern == s;
}
int len = pattern.length();
String[] sarray = s.split(" ");
if (len != sarray.length) {
return false;
}
Map<Character, String> pmap = new HashMap<>();
Map<String, Character> smap = new HashMap<>();
for (int i = 0; i < len; i++) {
char c = pattern.charAt(i);
String word = sarray[i];
if (!pmap.containsKey(c)) {
// smap contain, return false
if (smap.containsKey(word)) {
return false;
}
pmap.put(c, word);
smap.put(word, c);
} else {
String mapword = pmap.get(c);
if (!word.equals(mapword)) {
return false;
}
}
// build two map
pmap.put(c, word);
smap.put(word, c);
}
return true;
}
复杂度分析
时间复杂度: O(N)哪里 ññ表示中的字数s或中的字符数pattern。
空间复杂度: O(M) 哪里 中号中号代表中的唯一字数s。即使我们有两个哈希图,字符到单词哈希图的空间复杂度为O(1) 因为最多可以有26个键。
附录:我们不能保留两个哈希映射,而只能保留字符到单词的映射,并且只要我们发现不在映射中的字符,就可以检查当前字符-单词对中的单词是否已经是当前值中的一个。字符到单词的映射。但是,这是为了获得更好的空间而付出的时间,因为检查哈希映射中的值是O(M) 操作地点 中号中号是哈希图中的键值对的数量。因此,如果我们决定采用这种方式,我们的时间复杂度将是O(NM)哪里 ññ是中的唯一字符数pattern。
与方法1相似的另一种方法是使用哈希集来跟踪遇到的单词。无需检查单词是否已存在于单词到字符的映射中,只需检查单词是否在遇到的单词哈希集中。而且,您无需将单词更新为字符映射,只需将单词添加到遇到的单词哈希集即可。即使哈希集和哈希图的big-O空间复杂度相同,哈希集也将具有更好的实际空间复杂度。
方法2:单索引哈希图
直觉
而不是拥有两个哈希图,我们可以有一个索引哈希图,它跟踪中每个字符pattern和中每个单词的首次出现s。遍历每个字符-单词对时,我们插入看不见的字符pattern和看不见的词s。
目的是确保每个字符和单词的索引匹配。一旦发现不匹配,我们可以返回False。
让我们来看一些例子。
- pattern:‘abba’
- s:‘dog cat cat dog’
- ‘a’和’dog’-> map_index = {‘a’: 0, ‘dog’: 0}
索引“ a”和索引“ dog”相同。 - ‘b’和’cat’-> map_index = {‘a’: 0, ‘dog’: 0, ‘b’: 1, ‘cat’: 1}
索引“ b”和索引“ cat”相同。 - ‘b’和’cat’-> map_index = {‘a’: 0, ‘dog’: 0, ‘b’: 1, ‘cat’: 1}
“ b”已在映射中,无需更新。
“ cat”已经在映射中,无需更新。
索引“ b”和索引“ cat”相同。 - ‘a’和’dog’-> map_index = {‘a’: 0, ‘dog’: 0, ‘b’: 1, ‘cat’: 1}
“ a”已经在映射中,无需更新。
“ dog”已经在映射中,无需更新。
索引“ a”和索引“ dog”相同。
- pattern:‘abba’
- s:‘dog cat fish dog’
- ‘a’和’dog’-> map_index = {‘a’: 0, ‘dog’: 0}
索引“ a”和索引“ dog”相同。 - ‘b’和’cat’-> map_index = {‘a’: 0, ‘dog’: 0, ‘b’: 1, ‘cat’: 1}
索引“ b”和索引“ cat”相同。 - ‘b’和’fish’-> map_index = {‘a’: 0, ‘dog’: 0, ‘b’: 1, ‘cat’: 1, ‘fish’: 2}
“ b”已在映射中,无需更新。
“ b”的索引与“鱼”的索引不同。返回False。
实作
区分字符和字符串:在Python中没有单独的char类型。对于以下情况:
- pattern:‘abba’
- s:‘baab’
使用相同的哈希图将无法正常工作。一种解决方法是在每个字符前pattern添加“ char_”,在每个单词前s添加“ word_”。
这里需要注意:遍历必须用Integer,而不是int. 因为Integer是对象,相同的int装箱为不同的Integer。
public boolean wordPatternWithOneMap(String pattern, String s) {
// check edge
if (pattern == null || s == null) {
return pattern == s;
}
int len = pattern.length();
String[] sarray = s.split(" ");
if (len != sarray.length) {
return false;
}
Map<Object, Integer> map = new HashMap<>();
for (Integer i = 0; i < len; i++) {
char c = pattern.charAt(i);
String word = sarray[i];
if (!map.containsKey(c)) {
map.put(c, i);
}
if (!map.containsKey(word)) {
map.put(word, i);
}
if (map.get(c) != map.get(word)) {
return false;
}
}
return true;
}
复杂度分析
时间复杂度: 上)O(N) 哪里 ññ代表中的字数s或中的字符数pattern。
空间复杂度: O(M)中号是中的唯一字符pattern和中的单词的数量s。