1. 程式人生 > 其它 >leetcode 1177. 構建迴文串檢測

leetcode 1177. 構建迴文串檢測

給你一個字串 s,請你對 s 的子串進行檢測。

每次檢測,待檢子串都可以表示為 queries[i] = [left, right, k]。我們可以 重新排列 子串 s[left], ..., s[right],並從中選擇 最多 k 項替換成任何小寫英文字母。 

如果在上述檢測過程中,子串可以變成迴文形式的字串,那麼檢測結果為 true,否則結果為 false。

返回答案陣列 answer[],其中 answer[i] 是第 i 個待檢子串 queries[i] 的檢測結果。

注意:在替換時,子串中的每個字母都必須作為 獨立的 項進行計數,也就是說,如果 s[left..right] = "aaa" 且 k = 2,我們只能替換其中的兩個字母。(另外,任何檢測都不會修改原始字串 s,可以認為每次檢測都是獨立的)

 

示例:

輸入:s = "abcda", queries = [[3,3,0],[1,2,0],[0,3,1],[0,3,2],[0,4,1]]
輸出:[true,false,false,true,true]
解釋:
queries[0] : 子串 = "d",迴文。
queries[1] : 子串 = "bc",不是迴文。
queries[2] : 子串 = "abcd",只替換 1 個字元是變不成迴文串的。
queries[3] : 子串 = "abcd",可以變成迴文的 "abba"。 也可以變成 "baab",先重新排序變成 "bacd",然後把 "cd" 替換為 "ab"。
queries[4] : 子串 = "abcda",可以變成迴文的 "abcba"。
 

提示:

1 <= s.length, queries.length <= 10^5
0 <= queries[i][0] <= queries[i][1] < s.length
0 <= queries[i][2] <= s.length
s 中只有小寫英文字母

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/can-make-palindrome-from-substring
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

 

字首和(記錄奇偶即可) + 位運算

1:因為都是小寫字母,並且可以重排序,所以判斷迴文數的方法可以是統計區間內,26個字母的個數,若是隻有一個或者零個字母是奇數個,則此區間就是迴文數。

2:改變k個字元之後,變成迴文數,區間內可以最多有 2 * k + 1個字元是奇數個,即奇數字母個數 n <= 2 * k + 1

3:所以可以用字首和,建立陣列arr。 arr[i] 表示 從 str[0] 到 str[i]中每個字元出現的次數, 若想統計 a 到 b的區間中字元的個數,則可以用arr[a] - arr[b - 1]。

4:現在的問題變成了,怎麼高效的記錄字首和。從上面的解析,不難看出,我們無需知道每個字元的個數,只需要知道每個字元是奇數個,還是偶數個。

5:所以我們可以用一個int型的數字,來記錄每個字元的奇偶數,int型有32位,小寫字母只有26個,我們可以定義一種對映, 用 int的後26位來記錄26個字元的奇偶性。1代表奇數個,0代表偶數個。
例如:
"ab" -> 00000000000000000000000000000011
"bc" -> 00000000000000000000000000000110
"bcaa" -> 00000000000000000000000000000110
並且用異或,可以模擬求差值, 字首和的求取就是用異或。

6:當我們需要求a 到 b的區間中字元的奇偶性時,只需要int c = arr[a] ^ arr[b - 1] 即可。c的二進位制中1的個數,就是a 到 b的區間中奇數字符的個數。

7:若想要統計int中1的個數,可以直接呼叫 Integer.bitCount(c); 也可以用c & (c - 1)來統計。

 

    public List<Boolean> canMakePaliQueries(String s, int[][] queries) {
        int length = s.length();
        int[] arr = new int[length + 1];
        for (int i = 0; i < length; i++) {
            arr[i + 1] = arr[i] ^ (1 << (s.charAt(i) - 'a'));
        }
        List<Boolean> list = new ArrayList<>(queries.length);
        for (int[] query : queries) {
            int v = arr[query[1] + 1] ^ arr[query[0]];
            int c = 0;
            while (v != 0) {
                c++;
                v = v & (v - 1);
            }
            list.add(c <= (query[2] << 1) + 1);
        }
        return list;
    }