文章目录

  • Can Make Palindrome from Substring 构建回文串检测
    • 问题描述:
    • 分析
    • 代码

Can Make Palindrome from Substring 构建回文串检测

问题描述:

给你一个字符串 s,请你对 s 的子串进行检测。

每次检测,待检子串都可以表示为 queries[i] = [left, right, k]。我们可以 重新排列 子串 s[left], …, s[right],并从中选择 最多 k 项替换成任何小写英文字母。

如果在上述检测过程中,子串可以变成回文形式的字符串,那么检测结果为 true,否则结果为 false。

返回答案数组 answer[],其中 answer[i] 是第 i 个待检子串 queries[i] 的检测结果。

s.length,queries.length范围[1,10000]

s只有小写字母

分析

给出的字符串s,有不同的query区间,而对每个区间检查是否可以在排列后并且不超过k个修改的情况下符合回文串。
对于常见的回文验证,双指针是不错的方式,但是时间复杂度是 O ( N )O(N)O(N)
也就是每次的回文验证的时间开销取决于query的长度。但是问题中还允许重新排列子串,也就是说平常的回文验证是针对一个字符串,而这种情况下,要求对长度L的子字符串的所有排列进行验证。
如果任一排列可以满足回文验证,结果就是true。所以最简单的方式就是统计字符的频次。如果在长度L的子字符串中,频次是偶数,或者最多一个奇数,那么这个子字符串一定符合回文。
但是这种处理的方式,时间复杂度也是 O ( N )O(N)O(N)
而我们需要关注的是这个区间的字符的频次,很明显可以使用前缀和,将每个下标处字符出现的频次使用前缀和记录,然后可以通过下标之间的字符C,相减就可以得到这个区间的字符C的频次,预处理前缀和 O ( N )O(N)O(N),而计算的时间开销为 O ( 1 )O(1)O(1)
再进一步,因为都是小写字母,所以可以将异或运算把前缀和
,压缩到一个int32中。
当计算i~j区间的字符频次时,只需要统计 s u m [ j + 1 ] ⊕ s u m [ i ]sum[j+1]\oplus sum[i]sum[j+1]sum[i]的二进制1的数量cnt,cnt就是出现奇数次的字符数量, 如果 c n t < = 2 ∗ k + 1cnt<=2*k+1cnt<=2k+1,就可以得到一个符合条件的回文串,否则就是false。
因为当出现3个奇数频次的字符,那么修改一个字符就可以抵消2个奇数频次的字符,而只保留一个奇数频次的字符。

代码

public List<Boolean> canMakePaliQueries(String s, int[][] queries) {int n = s.length();int[] count = new int[n + 1];for (int i = 0; i < n; i++) {count[i + 1] = count[i] ^ (1 << (s.charAt(i) - 'a'));}List<Boolean> res = new ArrayList<>();for (int i = 0; i < queries.length; i++) {int l = queries[i][0], r = queries[i][1], k = queries[i][2];int bits = 0, x = count[r + 1] ^ count[l];while (x > 0) {x &= x - 1;bits++;}res.add(bits <= k * 2 + 1);}return res;}

时间复杂度 O(N+M) N是s的长度,M是queries的长度

空间复杂度: O(N)

Tag

Array Bit Manipulation Prefix Sum String