欢迎大家加QQ群:623375442,可以方便群里面交流。这周题目难度骤降。

100421. Find the Sequence of Strings Appeared on the Screen

给你一个字符串 target。

Alice 将会使用一种特殊的键盘在她的电脑上输入 target,这个键盘 只有两个 按键:

  • 按键 1:在屏幕上的字符串后追加字符 'a'。
  • 按键 2:将屏幕上字符串的 最后一个 字符更改为英文字母表中的 下一个 字符。例如,'c' 变为 'd','z' 变为 'a'。

注意,最初屏幕上是一个空字符串 "",所以她 只能 按按键 1。

请你考虑按键次数 最少 的情况,按字符串出现顺序,返回 Alice 输入 target 时屏幕上出现的所有字符串列表。

测试样例:

输入: target = "abc"

输出: ["a","aa","ab","aba","abb","abc"]

解释:Alice 按键的顺序如下:

  • 按下按键 1,屏幕上的字符串变为 "a"。
  • 按下按键 1,屏幕上的字符串变为 "aa"。
  • 按下按键 2,屏幕上的字符串变为 "ab"。
  • 按下按键 1,屏幕上的字符串变为 "aba"。
  • 按下按键 2,屏幕上的字符串变为 "abb"。
  • 按下按键 2,屏幕上的字符串变为 "abc"。

解答:这题就是按照题意暴力枚举一下Alice的按键顺序。

class Solution {
    public List<String> stringSequence(String target) {
        List<String> res = new ArrayList<>();
        int last = 0;
        for (int i = 0; i < target.length(); ++i) {
            char cur = target.charAt(i);
            for (char c = 'a'; c <= cur; ++c) {
                if (i == 0) {
                    res.add(String.valueOf(c));
                } else {
                    res.add(res.get(last) + c);
                }
            }
            last = res.size() - 1;
        }
        return res;
    }
}

100369. Count Substrings With K-Frequency Characters I

给你一个字符串 s 和一个整数 k,在 s 的所有子字符串中,请你统计并返回 至少有一个 字符 至少出现 k 次的子字符串总数。

子字符串 是字符串中的一个连续、 非空 的字符序列。

测试样例:

输入:s = "abacb", k = 2

输出:4

解释:符合条件的子字符串如下:

  • "aba"(字符 'a' 出现 2 次)。
  • "abac"(字符 'a' 出现 2 次)。
  • "abacb"(字符 'a' 出现 2 次)。
  • "bacb"(字符 'b' 出现 2 次)。

解答:双指针题。右指针不断前进,左指针联动。左指针指向第一个不满足要求的位置。

class Solution {
    public int numberOfSubstrings(String s, int k) {
        int[][] count = new int[s.length() + 1][26];
        for (int i = 0; i < s.length(); ++i) {
            for (int j = 0; j < 26; ++j) {
                count[i + 1][j] = count[i][j];
            }
            count[i + 1][s.charAt(i) - 'a'] += 1;
        }
        int st = 0, ed = 0;
        int res = 0;
        while (ed < s.length()) {
            while (st <= ed && isValid(st, ed, k, count)) {
                ++st;
            }
            if (st - 1 >= 0 && isValid(st - 1, ed, k, count)) {
                res += st;
            }
            ++ed;
        }
        return res;
    }

    private boolean isValid(int st, int ed, int k, int[][] count) {
        boolean res = false;
        for (int i = 0; i < 26; ++i) {
            if (count[ed + 1][i] - count[st][i] >= k) {
                res = true;
                break;
            }
        }
        return res;
    }
}

100453. Minimum Division Operations to Make Array Non Decreasing

给你一个整数数组 nums 。

一个正整数 x 的任何一个 严格小于 x 的 正 因子都被称为 x 的 真因数 。比方说 2 是 4 的 真因数,但 6 不是 6 的 真因数。

你可以对 nums 的任何数字做任意次 操作 ,一次 操作 中,你可以选择 nums 中的任意一个元素,将它除以它的 最大真因数 。

你的目标是将数组变为 非递减 的,请你返回达成这一目标需要的 最少操作 次数。

如果 无法 将数组变成非递减的,请你返回 -1 。

测试样例:

输入:nums = [25,7]

输出:1

解释:通过一次操作,25 除以 5 ,nums 变为 [5, 7] 。

解答:动态规划,每个位置有俩状态。不被选中(保持原始数字),选中(除以最大真因数)。我们从一个dp数组,保留上一个位置选中和不被选中,最小的操作数(不满足要求就放Integer.MAX_VALUE)。迭代计算。

class Solution {
    public int minOperations(int[] nums) {
        int[] res = {0,1};
        for (int i = 1; i < nums.length; ++i) {
            int[] nextRes = {Integer.MAX_VALUE, Integer.MAX_VALUE};
            // 上一个位置,不被选中满足要求
            if (res[0] != Integer.MAX_VALUE) {
                // 按照要求更新当前位置状态
                if (nums[i] >= nums[i - 1]) {
                    nextRes[0] = Math.min(nextRes[0], res[0]);
                }
                if (MaxDiv.getResult(nums[i]) >= nums[i - 1]) {
                    nextRes[1] = Math.min(nextRes[1], res[0] + 1);
                }
            }
            // 上一个位置,被选中满足要求
            if (res[1] != Integer.MAX_VALUE) {
                if (nums[i] >= MaxDiv.getResult(nums[i - 1])) {
                    nextRes[0] = Math.min(nextRes[0], res[1]);
                }
                if (MaxDiv.getResult(nums[i]) >= MaxDiv.getResult(nums[i - 1])) {
                    nextRes[1] = Math.min(nextRes[1], res[1] + 1);
                }
            }
            res = nextRes;
        }
        return Math.min(res[0], res[1]) != Integer.MAX_VALUE ? Math.min(res[0], res[1]) : -1;
    }
}

class MaxDiv {
    private static int[] shared = new int[1_000_001];
    static {
        Arrays.fill(shared, -1);
    }

    public static int getResult(int n) {
        if (shared[n] == -1) {
            shared[n] = n;
            for (int i = 2; i * i <= n; ++i) {
                if (n % i == 0) {
                    shared[n] = i;
                    break;
                }
            }
        }
        return shared[n];
    }
}

100449. Check if DFS Strings Are Palindromes

给你一棵 n 个节点的树,树的根节点为 0 ,n 个节点的编号为 0 到 n - 1 。这棵树用一个长度为 n 的数组 parent 表示,其中 parent[i] 是节点 i 的父节点。由于节点 0 是根节点,所以 parent[0] == -1 。

给你一个长度为 n 的字符串 s ,其中 s[i] 是节点 i 对应的字符。

一开始你有一个空字符串 dfsStr ,定义一个递归函数 dfs(int x) ,它的输入是节点 x ,并依次执行以下操作:

  • 按照 节点编号升序 遍历 x 的所有孩子节点 y ,并调用 dfs(y) 。
  • 将 字符 s[x] 添加到字符串 dfsStr 的末尾。

注意,所有递归函数 dfs 都共享全局变量 dfsStr 。

你需要求出一个长度为 n 的布尔数组 answer ,对于 0 到 n - 1 的每一个下标 i ,你需要执行以下操作:

  • 清空字符串 dfsStr 并调用 dfs(i) 。
  • 如果结果字符串 dfsStr 是一个 回文串 ,answer[i] 为 true ,否则 answer[i] 为 false 。

请你返回字符串 answer 。

回文串 指的是一个字符串从前往后与从后往前是一模一样的。

测试样例:

输入:parent = [-1,0,0,1,1,2], s = "aababa"

输出:[true,true,false,true,true,true]

解释:

  • 调用 dfs(0) ,得到字符串 dfsStr = "abaaba" ,是一个回文串。
  • 调用 dfs(1) ,得到字符串dfsStr = "aba" ,是一个回文串。
  • 调用 dfs(2) ,得到字符串dfsStr = "ab" ,不 是回文串。
  • 调用 dfs(3) ,得到字符串dfsStr = "a" ,是一个回文串。
  • 调用 dfs(4) ,得到字符串 dfsStr = "b" ,是一个回文串。
  • 调用 dfs(5) ,得到字符串 dfsStr = "a" ,是一个回文串。

解答:这题不太会做,只想到用Rolling Hash暴力计算了。

class Solution {
    public boolean[] findAnswer(int[] parent, String s) {
        List<Integer>[] edges = new List[parent.length];
        for (int i = 0; i < parent.length; ++i) {
            edges[i] = new ArrayList<>();
        }
        for (int i = 1; i < parent.length; ++i) {
            edges[parent[i]].add(i);
        }
        // 计算每个几点子节点数量
        int[] childCount = new int[parent.length];
        dfsCount(edges, 0, childCount);
        // 按照子节点信息,向最后的String填充
        char[] list = new char[s.length()];
        dfsCharList(edges, 0, s, 0, s.length() - 1, childCount, list);
        boolean[] res = new boolean[parent.length];
        // 利用Rolling Hash快速计算是否对称
        RangeHash hash1 = new RangeHash(list, false), hash2 = new RangeHash(list, true);
        dfsResult(edges, 0, hash1, hash2, 0, s.length() - 1, childCount, res);
        return res;
    }

    private void dfsCount(List<Integer>[] edges, int cur, int[] childCount) {
        if (edges[cur].isEmpty()) {
            childCount[cur] = 1;
        } else {
            childCount[cur] = 1;
            for (int n : edges[cur]) {
                dfsCount(edges, n, childCount);
                childCount[cur] += childCount[n];
            }
        }
    }

    private void dfsCharList(List<Integer>[] edges, int cur, String s, int st, int ed, int[] childCount, char[] list) {
        list[ed] = s.charAt(cur);
        int pos = st;
        for (int n : edges[cur]) {
            dfsCharList(edges, n, s, pos, pos + childCount[n] - 1, childCount, list);
            pos += childCount[n];
        }
    }

    private void dfsResult(List<Integer>[] edges, int cur, RangeHash hash1, RangeHash hash2, int st, int ed, int[] childCount, boolean[] res) {
        int len = hash1.getLen();
        long h1 = hash1.getRangeHash(st, ed), h2 = hash2.getRangeHash(len - 1 - ed, len - 1 - st);
        res[cur] = h1 == h2;
        int pos = st;
        for (int n : edges[cur]) {
            dfsResult(edges, n, hash1, hash2, pos, pos + childCount[n] - 1, childCount, res);
            pos += childCount[n];
        }
    }
}

// Rolling Hash模版
class RangeHash {
    private static final int mod = 1_000_000_007;
    private static final int inv = 129032259;
    public long[] prefix, div;

    public RangeHash(char[] word, boolean dir) {
        prefix = new long[word.length + 1];
        div = new long[word.length + 1];
        long mul = 1;
        div[0] = 1;
        int i = !dir ? 0 : word.length - 1;
        int pos = !dir ? 1 : -1;
        for (int j = 0; i < word.length && i >= 0; i += pos, ++j) {
            int t = word[i] - 'a';
            prefix[j + 1] = (prefix[j] + t * mul) % mod;
            mul = (mul * 31) % mod;
            div[j + 1] = div[j] * inv % mod;
        }
    }

    public long getRangeHash(int st, int ed) {
        long diff = (prefix[ed + 1] - prefix[st] + mod) % mod;
        return diff * div[st] % mod;
    }

    public int getLen() {
        return prefix.length - 1;
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *