leetcode
leetcode 851 ~ 900
戳印序列

戳印序列

难度:

标签:

题目描述

代码结果

运行时间: 51 ms, 内存: 16.4 MB


/*
 * 使用Java Stream的题解
 * 1. 与Java的解法类似,但是使用Stream来简化一些操作。
 */
import java.util.*;
import java.util.stream.*;

public class SolutionStream {
    public int[] movesToStamp(String stamp, String target) {
        char[] s = stamp.toCharArray();
        char[] t = target.toCharArray();
        int slen = s.length, tlen = t.length;
        List<Integer> result = new ArrayList<>();
        boolean[] visited = new boolean[tlen];
        int stars = 0;

        while (stars < tlen) {
            boolean doneReplace = false;
            for (int i = 0; i <= tlen - slen; i++) {
                if (!visited[i] && canReplace(t, i, s)) {
                    stars = doReplace(t, i, slen, stars);
                    doneReplace = true;
                    visited[i] = true;
                    result.add(i);
                    if (stars == tlen) {
                        break;
                    }
                }
            }
            if (!doneReplace) {
                return new int[0];
            }
        }

        Collections.reverse(result);
        return result.stream().mapToInt(i -> i).toArray();
    }

    private boolean canReplace(char[] t, int p, char[] s) {
        return IntStream.range(0, s.length).allMatch(i -> t[p + i] == '*' || t[p + i] == s[i]);
    }

    private int doReplace(char[] t, int p, int len, int stars) {
        stars += (int) IntStream.range(0, len).filter(i -> t[p + i] != '*').peek(i -> t[p + i] = '*').count();
        return stars;
    }
}

解释

方法:

这个题解采用了回溯的思路。从目标字符串的左端开始,先找到与印章开头相同的部分,然后递归地寻找右边剩余部分的合法分割。如果找不到,就回溯尝试更短的开头。当找到一个与印章完全相同的子串时,转而从右端开始寻找与印章结尾相同的部分,直到找到一个完整的合法分割。在回溯的过程中,用一个集合记录所有不能完成的开头坐标,避免重复搜索。

时间复杂度:

O(m^(n/m))

空间复杂度:

O(n^2/m)

代码细节讲解

🦆
在递归函数`recurtion`中,你是如何确保每次调用都能有效地减少问题的规模,从而避免无限递归?
在`recurtion`函数中,每次递归调用都是通过改变索引`idx`来确保问题规模的逐步减小。在成功匹配了印章的一部分后,将`idx`增加匹配的长度`l`,这样每次递归都是基于目标字符串的新的子字符串。此外,通过在集合`invalid`中记录那些已经被证实无法完成任务的起始点,可以防止在相同的位置重复进行无效的递归调用,这也有助于减少问题规模并避免无限递归。
🦆
函数`left`在寻找开头与stamp相同部分时,如果完全匹配的长度小于stamp的长度,这种部分匹配对整体解决方案有何影响?
当`left`函数中找到的匹配长度小于印章`stamp`的长度时,这表示只有部分印章与目标字符串相匹配。这种部分匹配可能导致无法直接在当前位置实现完整的印章覆盖,从而需要进一步的递归调用来尝试其他可能的匹配。这种部分匹配允许算法在不立即找到完整匹配的情况下,通过递归探索不同的匹配可能性,逐步构建出一个有效的戳印序列。
🦆
为什么在处理结尾相同的部分时,需要从右向左递归搜索,而不是继续从左向右?这样的方向变换对算法的效率和结果有什么具体影响?
在处理结尾相同的部分时,从右向左递归搜索是为了确保在构建戳印序列时可以有效地覆盖目标字符串的末端。从右向左的搜索可以帮助确认在已经部分匹配的情况下,目标字符串的剩余部分是否可以被继续有效地覆盖。这种方向的变换允许算法在遇到难以从左端开始完全匹配的情况时,从另一个方向尝试可能的解决方案,增加了算法的灵活性和覆盖率。从效率上讲,这种双向递归策略可以在某些情况下减少不必要的递归深度,更快地找到解决方案。

相关问题