You have a list of words
and a pattern
, and you want to know which words in words
matches the pattern.html
A word matches the pattern if there exists a permutation of letters p
so that after replacing every letter x
in the pattern with p(x)
, we get the desired word.git
(Recall that a permutation of letters is a bijection from letters to letters: every letter maps to another letter, and no two letters map to the same letter.)github
Return a list of the words in words
that match the given pattern. 數組
You may return the answer in any order.設計
Example 1:code
Input: words = ["abc","deq","mee","aqq","dkd","ccc"], pattern = "abb" Output: ["mee","aqq"] Explanation: "mee" matches the pattern because there is a permutation {a -> m, b -> e, ...}. "ccc" does not match the pattern because {a -> c, b -> c, ...} is not a permutation, since a and b map to the same letter.
Note:orm
1 <= words.length <= 50
1 <= pattern.length = words[i].length <= 20
這道題給了咱們一個字符串數組 words,還有一個 pattern 單詞,問 words 數組中的單詞是否知足 pattern 的模式,並給了一個例子。好比 pattern 是 abb 的話,表示後兩個字母是相同的,好比 mee 和 aqq,那麼一個很直接的想法就是創建每一個單詞 word 和 pattern 中每一個字符之間的映射,好比 mee->abb 的話,就是 m->a, e->b,在創建映射以前要判斷,若已經存在了該映射,且映射值不是當前 pattern 中的對應字符時,就是沒法匹配的,好比 mm 和 ab,在第一次創建了 m->a 的映射,當遍歷到第二個m的時候,發現m的映射已經存在,但不是b,就不能再創建 m->b 的映射,則表示沒法匹配。分析到這裏,你可能感受沒啥問題,但其實咱們忽略一種狀況,word 和 pattern 中的每一個字符必須是一一對應的,任何一個方向的多對一都是不行了,好比 mn 和 aa,剛開始創建了 m->a 的映射,遍歷到n的時候,發現沒有n的映射,此時也不能創建 n->a 的映射,由於 pattern 中的a已經被佔用了,因此還須要一個 HashMap 來創建反方向的映射,只有兩個 HashMap 中都不存在的,才能創建映射,只要有一個已經存在了,直接 break 掉。在 for 循環結束後,看是否已經到達了 word 的末尾,沒有提早 break 掉的話,就將 word 加入結果 res 中便可,參見代碼以下:htm
解法一:blog
class Solution { public: vector<string> findAndReplacePattern(vector<string>& words, string pattern) { vector<string> res; for (string word : words) { unordered_map<char, char> w2p, p2w; int i = 0, n = word.size(); for (; i < n; ++i) { if (w2p.count(word[i]) && w2p[word[i]] != pattern[i]) break; w2p[word[i]] = pattern[i]; if (p2w.count(pattern[i]) && p2w[pattern[i]] != word[i]) break; p2w[pattern[i]] = word[i]; } if (i == n) res.push_back(word); } return res; } };
咱們也能夠不用 HashMap,改用兩個長度爲 26 的數組,由於這道題貌似默認都是小寫字母,惟一麻煩一點的就是要把字母減去 'a' 來轉爲對應的座標,還有一點跟上面解法不一樣的地方就是,字母是跟起座標位置加1來創建映射(加1的緣由是默認值是0,而當 i=0 時爲了區分默認值,就要加1),由於兩個字母都跟一個特定的值相等,其實也等價於這兩個字母之間創建的映射(a->c, b->c => a->b)。總體思路仍是沒啥不一樣的,參見代碼以下:ci
解法二:
class Solution { public: vector<string> findAndReplacePattern(vector<string>& words, string pattern) { vector<string> res; for (string word : words) { vector<int> w(26), p(26); int i = 0, n = word.size(); for (; i < n; ++i) { if (w[word[i] - 'a'] != p[pattern[i] - 'a']) break; w[word[i] - 'a'] = p[pattern[i] - 'a'] = i + 1; } if (i == n) res.push_back(word); } return res; } };
在論壇上又看到了一種解法,這種解法至關於把全部的單詞都轉爲了一種特定的模式,具體來講,就是用一個 HashMap,創建每一個字母跟其以前出現過的字母種類個數以前的映射,好比 mee->011,aqq->011,這樣相同的模式映射的值是同樣的,具體的作法是若當前字母沒有出現過,則創建和當前 HashMap 中的映射個數之間的映射,是一種很巧妙的設計思路,只不過最後又給每一個數字加上了 'a',轉爲了字母的 pattern,即 mee->abb,aqq->abb,參見代碼以下:
解法三:
class Solution { public: vector<string> findAndReplacePattern(vector<string>& words, string pattern) { vector<string> res; for (string word : words) { if (helper(word) == helper(pattern)) res.push_back(word); } return res; } string helper(string word) { unordered_map<char, int> m; for (char c : word) { if (!m.count(c)) m[c] = m.size(); } for (int i = 0; i < word.size(); ++i) word[i] = 'a' + m[word[i]]; return word; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/890
相似題目:
參考資料:
https://leetcode.com/problems/find-and-replace-pattern/
https://leetcode.com/problems/find-and-replace-pattern/discuss/161266/JAVA-3ms-Clear-Code