【算法】Can Make Palindrome from Substring 构建回文串检测

文章讲述了如何解决检查字符串s的子串能否在限定替换次数k内变为回文串的问题。通过统计字符频次,使用前缀和与异或运算,可以在O(N+M)的时间复杂度内完成,其中N是字符串长度,M是查询数量。关键在于判断子串中奇数次字符数量是否不超过2k+1。

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 + 1 cnt<=2*k+1 cnt<=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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eric.Cui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值