这一题其实就是一道典型的backtrack的题目,每一个递归层的前进都伴随着一个指针在pattern上前进一格,然后另一个指针在str上前进n格(n = 1, 2, 3...到字符串结尾)。譬如pattern = "abab", str = "redblueredblue",第一个递归层往下走之后,对应pattern的指针只往前走一个,对应str的指针可以推进任意格造成任意的子字符串对应pattern第一个字符a。
每往下走一层,就分以下几种情况
1. 当前的pattern指针所对应的字母是否有对应的子字符串,有子字符串的话,你就知道str那边应该取多长的字符串与之匹配,如果配得上,就pattern指针往下走一格,str对应的指针往下走对应的格数,否则直接返回FALSE
2. 如果没有对应的子字符串,就按照上面的描述那样让当前的pattern指针指向的字符去匹配所有可能的子字符串并且往下走递归层。
思路并不复杂,可以得到代码如下:
public boolean wordPatternMatch(String pattern, String str) {
HashMap<Character, String> patternMap = new HashMap<>();
return _wordMatch(patternMap, new HashSet<>(), pattern, str, 0, 0);
}
private boolean _wordMatch(HashMap<Character, String> patternMap, HashSet<String> visited, String pattern, String str, int pPos, int sPos) {
if (pPos == pattern.length() && sPos == str.length()) {
return true;
} else if (pPos == pattern.length() || sPos == str.length()) {
return false;
} else {
char pCh = pattern.charAt(pPos);
if (patternMap.containsKey(pCh)) {
String matchedStr = patternMap.get(pCh);
if (str.length() - sPos < matchedStr.length() || !matchedStr.equals(str.substring(sPos, sPos + matchedStr.length()))) {
return false;
} else {
return _wordMatch(patternMap, visited, pattern, str, pPos + 1, sPos + matchedStr.length());
}
} else {
for (int i = sPos + 1; i <= str.length(); i++) {
String subStr = str.substring(sPos, i);
if (visited.contains(subStr)) continue;
patternMap.put(pCh, subStr);
visited.add(subStr);
if (_wordMatch(patternMap, visited, pattern, str, pPos + 1, i)) {
return true;
}
visited.remove(subStr);
patternMap.remove(pCh);
}
return false;
}
}
}
在上述代码中,patternMap就是记录你pattern中的每个字符在当前的情况下对应着什么字符串。所以每取一个子字符串放进map中就往下走一层,走完回归的时候就记得要删除。这其实就是一个DFS的思路。另外,我们还需要visited的原因是,pattern的字符和str的子字符串是一一对应的。譬如说如果pattern里a对应了red,那么b就不能对应red,所以如果该子字符串已经被对应过了,就不能再被pattern的其他字符对应了,所以我们就要跳过。
上面那个代码其实还可以优化一下,就是做一个简单的节支,在for循环里。如果你做完当前的匹配后,子字符串剩余的长度不足pattern字符串后续的长度,就无论如何都无法匹配下去了。譬如"abcde"和"applebanana",如果你"a"匹配了"applebana",那么pattern那边还有"bcde",但str那边就只剩下"na"了,怎么样匹配不上了。就可以直接略过
public boolean wordPatternMatch(String pattern, String str) {
HashMap<Character, String> patternMap = new HashMap<>();
return _wordMatch(patternMap, new HashSet<>(), pattern, str, 0, 0);
}
private boolean _wordMatch(HashMap<Character, String> patternMap, HashSet<String> visited, String pattern, String str, int pPos, int sPos) {
if (pPos == pattern.length() && sPos == str.length()) {
return true;
} else if (pPos == pattern.length() || sPos == str.length()) {
return false;
} else {
char pCh = pattern.charAt(pPos);
if (patternMap.containsKey(pCh)) {
String matchedStr = patternMap.get(pCh);
if (str.length() - sPos < matchedStr.length() || !matchedStr.equals(str.substring(sPos, sPos + matchedStr.length()))) {
return false;
} else {
return _wordMatch(patternMap, visited, pattern, str, pPos + 1, sPos + matchedStr.length());
}
} else {
for (int i = sPos + 1; i <= str.length() && str.length() - i >= pattern.length() - pPos - 1; i++) {
String subStr = str.substring(sPos, i);
if (visited.contains(subStr)) continue;
patternMap.put(pCh, subStr);
visited.add(subStr);
if (_wordMatch(patternMap, visited, pattern, str, pPos + 1, i)) {
return true;
}
visited.remove(subStr);
patternMap.remove(pCh);
}
return false;
}
}
}