[Leetcode] 291. Word Pattern II

本篇博客详细介绍了LeetCode中的291题,即Word Pattern II的解决方案。这是一个使用回溯(backtrack)算法的典型问题。在递归过程中,会有一个指针在模式(pattern)上逐步移动,同时另一个指针在输入字符串(str)上按步长n移动。解题思路分为两种情况:1. 如果当前模式字符已有匹配的子字符串,就继续匹配;2. 若无匹配子字符串,则尝试所有可能的子字符串进行匹配。整个过程采用深度优先搜索(DFS)策略,并通过patternMap记录模式字符与字符串子串的映射,同时用visited避免重复匹配。最后,代码中还加入了剪枝操作,避免无效的匹配尝试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一题其实就是一道典型的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;
            }
        }
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值