LeetCode 2983. 回文串重新排列查询

2983. 回文串重新排列查询

给你一个长度为 偶数 n ,下标从 0 开始的字符串 s 。

同时给你一个下标从 0 开始的二维整数数组 queries ,其中 queries[i] = [ai, bi, ci, di] 。

对于每个查询 i ,你需要执行以下操作:

  • 将下标在范围 0 <= ai <= bi < n / 2 内的 子字符串 s[ai:bi] 中的字符重新排列。
  • 将下标在范围 n / 2 <= ci <= di < n 内的 子字符串 s[ci:di] 中的字符重新排列。

对于每个查询,你的任务是判断执行操作后能否让 s 变成一个 回文串 。

每个查询与其他查询都是 独立的 。

请你返回一个下标从 0 开始的数组 answer ,如果第 i 个查询执行操作后,可以将 s 变为一个回文串,那么 answer[i] = true,否则为 false 。

  • 子字符串 指的是一个字符串中一段连续的字符序列。
  • s[x:y] 表示 s 中从下标 x 到 y 且两个端点 都包含 的子字符串。

示例 1:

输入:s = "abcabc", queries = [[1,1,3,5],[0,2,5,5]]
输出:[true,true]
解释:这个例子中,有 2 个查询:
第一个查询:
- a0 = 1, b0 = 1, c0 = 3, d0 = 5
- 你可以重新排列 s[1:1] => abcabc 和 s[3:5] => abcabc 。
- 为了让 s 变为回文串,s[3:5] 可以重新排列得到 => abccba 。
- 现在 s 是一个回文串。所以 answer[0] = true 。
第二个查询:
- a1 = 0, b1 = 2, c1 = 5, d1 = 5.
- 你可以重新排列 s[0:2] => abcabc 和 s[5:5] => abcabc 。
- 为了让 s 变为回文串,s[0:2] 可以重新排列得到 => cbaabc 。
- 现在 s 是一个回文串,所以 answer[1] = true 。

示例 2:

输入:s = "abbcdecbba", queries = [[0,2,7,9]]
输出:[false]
解释:这个示例中,只有一个查询。
a0 = 0, b0 = 2, c0 = 7, d0 = 9.
你可以重新排列 s[0:2] => abbcdecbba 和 s[7:9] => abbcdecbba 。
无法通过重新排列这些子字符串使 s 变为一个回文串,因为 s[3:6] 不是一个回文串。
所以 answer[0] = false 。

示例 3:

输入:s = "acbcab", queries = [[1,2,4,5]]
输出:[true]
解释:这个示例中,只有一个查询。
a0 = 1, b0 = 2, c0 = 4, d0 = 5.
你可以重新排列 s[1:2] => acbcab 和 s[4:5] => acbcab 。
为了让 s 变为回文串,s[1:2] 可以重新排列得到 => abccab。
然后 s[4:5] 重新排列得到 abccba 。
现在 s 是一个回文串,所以 answer[0] = true 。

提示:

  • 2 <= n == s.length <= 10^5
  • 1 <= queries.length <= 10^5
  • queries[i].length == 4
  • ai == queries[i][0], bi == queries[i][1]
  • ci == queries[i][2], di == queries[i][3]
  • 0 <= ai <= bi < n / 2
  • n / 2 <= ci <= di < n
  • n 是一个偶数。
  • s 只包含小写英文字母。

提示 1

Consider two indices, x on the left side and its symmetrical index y on the right side.


提示 2

Store the frequencies of all of the letters in both intervals [ai, bi] and [ci, di] in a query.


提示 3

If x is not in [ai, bi] and y is not in [ci, di], they must be the same.


提示 4

If x is in [ai, bi] and y is not in [ci, di], remove one occurrence of the character at index y from the frequency array on the left side.


提示 5

Similarly, if x is not in [ai, bi] and y is in [ci, di], remove one occurrence of the character at index x from the frequency array on the right side.


提示 6

Finally, check whether the two frequency arrays are the same, and the indices that don't fall into any of the intervals are the same as well.


提示 7

Use prefix-sum + hashing to improve the time complexity.

 

解法:前缀和 + 哈希表 / 频率数组

提示 1

考虑两个下标,左边是 x,右边是其对称下标 y。

提示 2

在查询中存储 [ai, bi] 和 [ci, di] 两个区间中所有字母的频率。

提示 3

如果 x 不在 [ai,bi] 中,而 y 不在 [ci,di] 中,那么它们必须是相同的。

提示 4

如果 x 在 [ai,bi] 中,而 y 不在 [ci,di]中,则从左侧的频率数组中删除索引为 y 的字符的一次出现。

提示 5

同样,如果 x 不在 [ai,bi] 中,而 y 在 [ci,di]中,则从右侧的频率数组中删除索引 x 处的一个字符。

提示 6

最后,检查两个频率数组是否相同,以及不属于任何区间的索引是否也相同。

提示 7

使用前缀和 + 哈希算法来提高时间复杂度。

Java版:

频率数组写法:

class Solution {
    public boolean[] canMakePalindromeQueries(String s, int[][] queries) {
        int n = s.length();
        int[][] counts = new int[n + 1][26];
        for (int i = 0; i < n; i++) {
            System.arraycopy(counts[i], 0, counts[i + 1], 0, 26);
            counts[i + 1][s.charAt(i) - 'a']++;
        }
        int m = queries.length;
        boolean[] ans = new boolean[m];
        for (int i = 0; i < m; i++) {
            if (i > 0 && Arrays.equals(queries[i], queries[i - 1])) {
                ans[i] = ans[i - 1];
                continue;
            }
            int[] leftmap = new int[26];
            int[] rightmap = new int[26];
            int a = queries[i][0];
            int b = queries[i][1];
            int c = queries[i][2];
            int d = queries[i][3];
            for (int j = 0; j < 26; j++) {
                leftmap[j] = counts[b + 1][j] - counts[a][j];
                rightmap[j] = counts[d + 1][j] - counts[c][j];
            }
            int x = 0;
            int y = n - 1;
            boolean check = true;
            while (x < y) {
                if ((x < a || x > b) && (y < c || y > d)) {
                    if (s.charAt(x) != s.charAt(y)) {
                        check = false;
                        break;
                    }
                } else if ( (x >= a && x <= b) && (y < c || y > d) ) {
                    int del = s.charAt(y) - 'a';
                    if (leftmap[del] > 0) {
                        leftmap[del]--;
                    } else {
                        check = false;
                        break;
                    }
                } else if ( (x < a || x > b) && (y >= c && y <= d)) {
                    int del = s.charAt(x) - 'a';
                    if (rightmap[del] > 0) {
                        rightmap[del]--;
                    } else {
                        check = false;
                        break;
                    }
                }
                x++;
                y--;
            }
            if (check == false) {
                ans[i] = false;
                continue;
            } 
            if (Arrays.equals(leftmap, rightmap)) {
                ans[i] = true;
            }
        }
        return ans;
    }
}

哈希表写法:

class Solution {
    public boolean[] canMakePalindromeQueries(String s, int[][] queries) {
        int n = s.length();
        int[][] counts = new int[n + 1][26];
        for (int i = 0; i < n; i++) {
            System.arraycopy(counts[i], 0, counts[i + 1], 0, 26);
            counts[i + 1][s.charAt(i) - 'a']++;
        }
        int m = queries.length;
        boolean[] ans = new boolean[m];
        for (int i = 0; i < m; i++) {
            if (i > 0 && Arrays.equals(queries[i], queries[i - 1])) {
                ans[i] = ans[i - 1];
                continue;
            }
            Map<Integer, Integer> leftmap = new HashMap<>();
            Map<Integer, Integer> rightmap = new HashMap<>();
            int a = queries[i][0];
            int b = queries[i][1];
            int c = queries[i][2];
            int d = queries[i][3];
            for (int j = 0; j < 26; j++) {
                if (counts[b + 1][j] - counts[a][j] > 0) {
                    leftmap.put(j, counts[b + 1][j] - counts[a][j]);
                }
                if (counts[d + 1][j] - counts[c][j] > 0) {
                    rightmap.put(j, counts[d + 1][j] - counts[c][j]);
                }
            }
            int x = 0;
            int y = n - 1;
            boolean check = true;
            while (x < y) {
                if ((x < a || x > b) && (y < c || y > d)) {
                    if (s.charAt(x) != s.charAt(y)) {
                        check = false;
                        break;
                    }
                } else if ( (x >= a && x <= b) && (y < c || y > d) ) {
                    int del = s.charAt(y) - 'a';
                    if (leftmap.containsKey(del)) {
                        if (leftmap.merge(del, -1, Integer::sum) == 0) {
                            leftmap.remove(del);
                        }
                    } else {
                        check = false;
                        break;
                    }
                } else if ( (x < a || x > b) && (y >= c && y <= d)) {
                    int del = s.charAt(x) - 'a';
                    if (rightmap.containsKey(del)) {
                        if (rightmap.merge(del, -1, Integer::sum) == 0) {
                            rightmap.remove(del);
                        }
                    } else {
                        check = false;
                        break;
                    }
                }
                x++;
                y--;
            }
            if (check == false) {
                ans[i] = false;
                continue;
            } 
            if (leftmap.equals(rightmap)) {
                ans[i] = true;
            }
        }
        return ans;
    }
}

Python3版:

class Solution:
    def canMakePalindromeQueries(self, s: str, queries: List[List[int]]) -> List[bool]:
        n = len(s)
        counts = [[0] * 26 for _ in range(n + 1)]
        for i, c in enumerate(s):
            counts[i + 1] = counts[i].copy()
            counts[i + 1][ord(c) - ord('a')] += 1

        ans = [False] * len(queries)
        for i, query in enumerate(queries):
            if i > 0 and queries[i] == queries[i - 1]:
                ans[i] = ans[i - 1]
                continue

            a, b, c, d = query[0], query[1], query[2], query[3]
            leftmap = [0] * 26
            rightmap = [0] * 26
            for j in range(26):
                leftmap[j] = counts[b + 1][j] - counts[a][j]
                rightmap[j] = counts[d + 1][j] - counts[c][j]

            x = 0
            y = n - 1
            check = True
            while x < y:
                if (x < a or x > b) and (y < c or y > d):
                    if s[x] != s[y]:
                        check =  False
                        break
                elif (x >= a and x <= b) and (y < c or y > d):
                    delete = ord(s[y]) - ord('a')
                    if leftmap[delete] > 0:
                        leftmap[delete] -= 1
                    else:
                        check = False 
                        break
                elif (x < a or x > b) and (y >= c and y <= d):
                    delete = ord(s[x]) - ord('a')
                    if rightmap[delete] > 0:
                        rightmap[delete] -= 1
                    else:
                        check = False 
                        break
                x += 1
                y -= 1

            if not check:
                ans[i] = False 
                continue 
            if leftmap == rightmap:
                ans[i] = True 
        
        return ans

            

 

复杂度分析

  • 时间复杂度:O(nA + m(n + A) ),其中 n 为 s 的长度,m 为 queries 的长度,A 为字符集合的大小,本题中字符均为小写字母,所以 A=26。回答每个询问的时间是 O(A)。
  • 空间复杂度:O(nA + m)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值