291. Word Pattern II
Given a pattern and a string str, find if str 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 substring in str.
Example 1:
Input: pattern = “abab”, str = “redblueredblue”
Output: true
Example 2:
Input: pattern = pattern = “aaaa”, str = “asdasdasdasd”
Output: true
Example 3:
Input: pattern = “aabb”, str = “xyzabcxzyabc”
Output: false
Notes:
You may assume both pattern and str contains only lowercase letters.
方法1: backtracking
思路:
和前一题相比,这道题str并没有被空格分开,需要一次次的尝试截断单词,并建立映射关系,每当有违反映射的情况出现,一层层的退回尝试新的截断方式。这就确定了backtracking的形式。在递归函数中传递一个patter和string的起始位置,终止条件是两个index同时遍历到尽头,标志着所有的映射都成立,可以返回true。但如果有一个已经遍历完了另一个却没有,那么可以肯定的返回false。否则冲pattern中先提取新的char c,然后尝试从s开始截断各种长度的string,判断有没有违反映射。第一种没有违反映射的条件是,之前已经建立过hash[c] = word,这里不用新建,直接向下递归。而第二种条件是,没有建立过这个映射,并且seen也没有见过这个word,i.e. 没有被映射到别的字母上过,那么就开始一次backtracking,向下递归。注意把两种成立条件一起处理是不对的,非本层建立的映射不能够在本层删除。另外这个递归的判断条件很容易遗漏情况,下面两种解法是对的。
易错点
- 终止条件。
- 判断双向映射都没有被违反。
- 没有违反的话,也要根据映射是否是第一次建立来决定要不要backtraking。
- 判断条件容易遗漏。
Complexity
Time complexity: O(2^n)
Space complexity: O(n)
class Solution1 {
public:
bool wordPatternMatch(string pattern, string str) {
unordered_map<char, string> hash;
set<string> seen;
return wordHelper(pattern, 0, str, 0, hash, seen);
}
bool wordHelper(string pattern, int p, string str, int s, unordered_map<char, string> & hash, set<string> & seen) {
if (p == pattern.size() && s == str.size()) return true;
if (p == pattern.size() || s == str.size()) return false;
char c = pattern[p];
for (int i = s; i < str.size(); i++) {
string word = str.substr(s, i - s + 1);
if (hash.count(c) && hash[c] == word) {
if (wordHelper(pattern, p + 1, str, i + 1, hash, seen)) return true;
}
else if (!hash.count(c)) {
if (seen.count(word)) continue;
hash[c] = word;
seen.insert(word);
if (wordHelper(pattern, p + 1, str, i + 1, hash, seen)) return true;
hash.erase(c);
seen.erase(word);
}
}
return false;
}
};
class Solution {
public:
bool wordPatternMatch(string pattern, string str) {
unordered_map<char, string> hash;
set<string> seen;
return wordHelper(pattern, 0, str, 0, hash, seen);
}
bool wordHelper(string pattern, int p, string str, int s, unordered_map<char, string> & hash, set<string> & seen) {
if (p == pattern.size() && s == str.size()) return true;
if (p == pattern.size() || s == str.size()) return false;
char c = pattern[p];
for (int i = s; i < str.size(); i++) {
string word = str.substr(s, i - s + 1);
if (hash.count(c) && hash[c] == word) {
if (wordHelper(pattern, p + 1, str, i + 1, hash, seen)) return true;
}
else if (!hash.count(c) && seen.count(word)) continue;
else {
if (hash.count(c)) continue;
hash[c] = word;
seen.insert(word);
if (wordHelper(pattern, p + 1, str, i + 1, hash, seen)) return true;
hash.erase(c);
seen.erase(word);
}
}
return false;
}
};
另外有一种不用seen来检查逆向映射被违反的情况,每次遍历hash来找有word有没有被映射到别的字母上,居然比上面还要快。
class Solution {
public:
bool wordPatternMatch(string pattern, string str) {
unordered_map<char, string> m;
return helper(pattern, 0, str, 0, m);
}
bool helper(string pattern, int p, string str, int r, unordered_map<char, string> &m) {
if (p == pattern.size() && r == str.size()) return true;
if (p == pattern.size() || r == str.size()) return false;
char c = pattern[p];
for (int i = r; i < str.size(); ++i) {
string t = str.substr(r, i - r + 1);
if (m.count(c) && m[c] == t) {
if (helper(pattern, p + 1, str, i + 1, m)) return true;
} else if (!m.count(c)) {
bool b = false;
for (auto it : m) {
if (it.second == t) b = true;
}
if (!b) {
m[c] = t;
if (helper(pattern, p + 1, str, i + 1, m)) return true;
m.erase(c);
}
}
}
return false;
}
};