leetcode
leetcode 2001 ~ 2050
替换字符后匹配

替换字符后匹配

难度:

标签:

题目描述

代码结果

运行时间: 67 ms, 内存: 16.8 MB


/* 
 * 思路: 
 * 1. 使用 Java Stream 构建从 old 到 new 的映射表。 
 * 2. 生成所有可能的 sub 替换形式。 
 * 3. 过滤出在 s 中的子串。 
 */ 
import java.util.*; 
import java.util.stream.*; 
public class Solution { 
    public boolean canTransform(String s, String sub, char[][] mappings) { 
        // 构建映射表 
        Map<Character, List<Character>> map = Arrays.stream(mappings) 
                .collect(Collectors.groupingBy(m -> m[0], 
                        Collectors.mapping(m -> m[1], Collectors.toList()))); 
        // 生成所有可能的字符串 
        return generateAll(sub.toCharArray(), map, 0).stream() 
                .anyMatch(s::contains); 
    } 
    private List<String> generateAll(char[] sub, Map<Character, List<Character>> map, int index) { 
        if (index == sub.length) { 
            return Collections.singletonList(new String(sub)); 
        } 
        List<String> results = new ArrayList<>(); 
        char original = sub[index]; 
        results.addAll(generateAll(sub, map, index + 1)); 
        if (map.containsKey(original)) { 
            for (char replacement : map.get(original)) { 
                sub[index] = replacement; 
                results.addAll(generateAll(sub, map, index + 1)); 
                sub[index] = original; 
            } 
        } 
        return results; 
    } 
}

解释

方法:

该题解使用了位运算和位掩码的方法来检查是否可以通过替换使得子字符串 `sub` 匹配字符串 `s` 的某一部分。首先,使用两个大小为128的数组(因为ASCII字符的范围是0-127),`old` 和 `new` 来表示字符在字符串 `s` 中出现的位置。`new` 数组通过位移和按位或操作记录每个字符在 `s` 中的位置。然后,使用 `mappings` 更新 `old` 数组,使得如果字符 `c1` 可以被映射为 `c2`,则 `c1` 的位置信息也会包含 `c2` 的位置信息。最后,通过按位与操作检查 `sub` 是否为 `s` 的子字符串。如果 `sub` 中的每个字符都可以在 `s` 中找到对应位置,且这些位置是连续的,则返回 `true`。

时间复杂度:

O(n + m + k)

空间复杂度:

O(1)

代码细节讲解

🦆
题解中提到使用位运算记录字符在字符串中的位置,请问如何理解这种位运算的具体实现细节?
在题解中,位运算用于高效地记录和查询每个字符在字符串`s`中的所有位置。具体来说,使用一个长度为128的数组`new`,每个位置对应ASCII表中一个字符。对于字符串`s`中的每个字符,使用`ord(c)`得到该字符的ASCII码作为数组索引。然后,将整数1左移`i`位,其中`i`是字符在字符串`s`中的索引位置,通过按位或操作`|=`,将这个位标记到`new[ord(c)]`中。这样,`new[ord(c)]`的每个位代表字符`c`在字符串`s`中的具体位置(如果某位是1,表示该字符出现在对应的位置上)。
🦆
为什么题解选择使用大小为128的数组来记录字符位置,是否考虑过使用其他数据结构,比如哈希表?
题解中选用128大小的数组是因为ASCII字符集包含128个字符,从0到127。使用固定大小的数组可以直接通过字符的ASCII码快速访问,操作简单且效率高。相比之下,使用哈希表虽然在处理非ASCII字符集时更为灵活,但对于ASCII字符,哈希表的额外空间和时间开销(处理哈希冲突和动态扩容)在本题中是不必要的,因此选择数组可以提供更优的性能。
🦆
在处理`mappings`的时候,更新`old`数组的操作`old[ord(c1)] |= new[ord(c2)]`是如何确保`sub`中的每个字符都可以被替换并匹配的?
题解中的操作`old[ord(c1)] |= new[ord(c2)]`是将字符`c2`在字符串`s`中的所有位置信息添加到字符`c1`的位置信息中。这样的映射更新确保如果`mappings`中指定`c1`可以被替换为`c2`,那么在尝试匹配子字符串`sub`时,`c1`的位置也会考虑`c2`的所有可能位置。这意味着,在检查`sub`是否为`s`的子字符串时,即使某个位置原本是由`c2`占据,只要`mappings`允许将`c2`替换为`c1`,这个位置也可以被视为有效的`c1`位置。因此,这种更新方式支持了字符的灵活替换和匹配。

相关问题