leetcode
leetcode 2701 ~ 2750
子字符串异或查询

子字符串异或查询

难度:

标签:

题目描述

You are given a binary string s, and a 2D integer array queries where queries[i] = [firsti, secondi].

For the ith query, find the shortest substring of s whose decimal value, val, yields secondi when bitwise XORed with firsti. In other words, val ^ firsti == secondi.

The answer to the ith query is the endpoints (0-indexed) of the substring [lefti, righti] or [-1, -1] if no such substring exists. If there are multiple answers, choose the one with the minimum lefti.

Return an array ans where ans[i] = [lefti, righti] is the answer to the ith query.

A substring is a contiguous non-empty sequence of characters within a string.

 

Example 1:

Input: s = "101101", queries = [[0,5],[1,2]]
Output: [[0,2],[2,3]]
Explanation: For the first query the substring in range [0,2] is "101" which has a decimal value of 5, and 5 ^ 0 = 5, hence the answer to the first query is [0,2]. In the second query, the substring in range [2,3] is "11", and has a decimal value of 3, and 3 ^ 1 = 2. So, [2,3] is returned for the second query. 

Example 2:

Input: s = "0101", queries = [[12,8]]
Output: [[-1,-1]]
Explanation: In this example there is no substring that answers the query, hence [-1,-1] is returned.

Example 3:

Input: s = "1", queries = [[4,5]]
Output: [[0,0]]
Explanation: For this example, the substring in range [0,0] has a decimal value of 1, and 1 ^ 4 = 5. So, the answer is [0,0].

 

Constraints:

  • 1 <= s.length <= 104
  • s[i] is either '0' or '1'.
  • 1 <= queries.length <= 105
  • 0 <= firsti, secondi <= 109

代码结果

运行时间: 107 ms, 内存: 57.3 MB


/*
 * Approach using Java Streams:
 * 1. Generate all possible substrings of the binary string s.
 * 2. Use streams to filter and find the shortest substring matching the query conditions.
 * 3. Return the indices of the substring or [-1, -1] if no such substring exists.
 */

import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

public class Solution {
    public int[][] solveQueries(String s, int[][] queries) {
        int n = s.length();
        int[][] result = new int[queries.length][2];

        for (int i = 0; i < queries.length; i++) {
            int first = queries[i][0];
            int second = queries[i][1];
            result[i][0] = -1;
            result[i][1] = -1;
            int minLength = Integer.MAX_VALUE;

            for (int start = 0; start < n; start++) {
                int finalI = i;
                IntStream.range(start, n).forEach(end -> {
                    String subStr = s.substring(start, end + 1);
                    int val = Integer.parseInt(subStr, 2);
                    if ((val ^ first) == second && (end - start + 1) < minLength) {
                        result[finalI][0] = start;
                        result[finalI][1] = end;
                        minLength = end - start + 1;
                    }
                });
            }
        }
        return result;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        String s = "101101";
        int[][] queries = {{0, 5}, {1, 2}};
        int[][] result = solution.solveQueries(s, queries);
        for (int[] res : result) {
            System.out.println("[" + res[0] + ", " + res[1] + "]");
        }
    }
}

解释

方法:

这个题解的思路基于预处理和哈希表的使用。首先,题解构建一个哈希表来存储所有可能的子字符串及其对应的起始和结束位置。算法首先检测字符串中的第一个'0'字符,并记录其位置(只记录第一个零是因为任何以0开始的子字符串的十进制值都是0)。然后,对于字符串中的每个'1'开始的位置,算法计算以该位置开始的所有可能长度(最长为30位,足以表示所有整数)的子字符串的十进制值,并将这些值及其对应的起始和结束位置存储在哈希表中。最后,对于每个查询,算法通过计算查询的异或值是否存在于哈希表中来直接返回答案,从而避免了多次计算。

时间复杂度:

O(n + q)

空间复杂度:

O(n + q)

代码细节讲解

🦆
为什么在处理二进制字符串时,只关注以'1'开始的子字符串而忽略以'0'开始的子字符串?
在处理二进制字符串时,以'0'开始的子字符串在二进制转十进制计算中,较长的前导0对值没有贡献,因此它们通常不会影响最终的十进制值,直到出现第一个'1'。例如,子字符串'0001'与'1'的十进制值相同。此外,题解中已经通过记录第一个0来处理所有以0开始的子字符串异或的情况,因为0异或任何数等于那个数本身。因此,算法优化通过跳过这些无关紧要的0,直接从第一个1开始计算,以提高效率和减少不必要的计算。
🦆
题解中提到的'记录第一个0的位置,因为0异或任何数都是那个数'这一点是否准确?0异或一个数确实得到该数,但这是否意味着我们只需记录第一个0的位置?
题解中提到的这一点是准确的。在二进制异或运算中,0异或任何数确实得到该数本身。因此,若查询中出现0,其结果直接为查询的另一部分数值,无需进一步计算。题解中只记录第一个0的位置是基于优化考虑,因为任何以0为起始的子字符串,其异或结果均为查询的另一部分数值,无需重复记录或计算。这样做可以简化数据结构并加快查询速度。
🦆
题解中提到计算子字符串十进制值的循环是最多30位长,这个长度30是如何确定的?是否有特定的理由选择30作为长度上限?
题解中选择30位作为子字符串的最大长度是基于二进制表示的整数范围考虑。在计算机中,一个常用的整数表示范围是32位整数,其中30位足以覆盖从0到2^30-1的所有整数,这个范围包括大约10亿个数值。对于大多数实际应用和算法竞赛,这个长度已经足够用来处理大部分情况。此外,设置一个合理的长度上限可以防止在极端情况下算法性能下降,例如避免在一个很长的全'1'字符串中进行不必要的计算。

相关问题