[LeetCode] 1177. Can Make Palindrome from Substring 構建回文串檢測



Given a string s, we make queries on substrings of s.

For each query queries[i] = [left, right, k], we may rearrange the substring s[left], ..., s[right], and then choose up to k of them to replace with any lowercase English letter.

If the substring is possible to be a palindrome string after the operations above, the result of the query is true. Otherwise, the result is false.

Return an array answer[], where answer[i] is the result of the i-th query queries[i].

Note that: Each letter is counted individually for replacement so if for example s[left..right] = "aaa", and k = 2, we can only replace two of the letters.  (Also, note that the initial string s is never modified by any query.)

Example :

Input: s = "abcda", queries = [[3,3,0],[1,2,0],[0,3,1],[0,3,2],[0,4,1]]
Output: [true,false,false,true,true]
Explanation:
queries[0] : substring = "d", is palidrome.
queries[1] : substring = "bc", is not palidrome.
queries[2] : substring = "abcd", is not palidrome after replacing only 1 character.
queries[3] : substring = "abcd", could be changed to "abba" which is palidrome. Also this can be changed to "baab" first rearrange it "bacd" then replace "cd" with "ab".
queries[4] : substring = "abcda", could be changed to "abcba" which is palidrome.

Constraints:

  • 1 <= s.length, queries.length <= 10^5
  • 0 <= queries[i][0] <= queries[i][1] < s.length
  • 0 <= queries[i][2] <= s.length
  • s only contains lowercase English letters.

這道題給了一個只有小寫字母的字符串s,讓對s對子串進行查詢。查詢塊包含三個信息,left,right 和k,其中 left 和 right 定義了子串的范圍,k表示可以進行替換字母的個數。這里希望通過替換可以將子串變為回文串,關於回文串想必各位刷題老司機都不陌生吧,就是正讀反讀都一樣,比如 noon,bob 等等。題目中還說了可以事先給子串進行排序,這個條件一加,整個性質就不一樣了,若不能排序,那么要替換的字母可能就很多了,因為對應的位置上的字母要相同才行。而能排序之后,只要把相同的字母盡可能的排列到對應的位置上,就可以減少要替換的字母,比如 hunu,若不能重排列,則至少要替換兩個字母才行,而能重排順序的話,可以先變成 uhnu,再替換中間的任意一個字母就可以了。而仔細觀察,需要替換的情況都是字母出現次數為奇數的情況,偶數的字母完全不用擔心,所以只要統計出出現次數為奇數的字母的個數,其除以2就是要替換的次數。那可能有的童鞋會問了,萬一是奇數怎么辦,除以2除不盡怎么辦,這是個好問題。若出現次數為奇數的字母的個數為奇數,則表明該子串的長度為奇數,而奇數回文串最中間的字母是不需要有對稱位置的,所以自然可以少替換一個,所以除不盡的部分就自動舍去了,並不影響最終的結果。講到這里,應該就不難做了,但博主最先寫的解法超時 TLE 了,做法是取出每個要查詢的子串,然后統計出現奇數次的字母個數,再除以2跟k比較。這種方法並不高效,可能會存在大量的重復計算。就比如求子數組之和一樣,若對於每個給定的子數組,都遍歷一遍求和,確實不高效,一般都是建立累加和數組來做。這里也可以借鑒類似的想法,不過這里是對每個子串都建立字母出現次數的映射,所以這里用一個二維數組,大小為 n+1 by 26,因為限定了只有小寫字母。然后遍歷字符串s進行更新,每次先將 cnt[i+1] 賦值為 cnt[i],然后在對應的字母位置映射值自增1。累加好了之后,對於任意區間 [i, j] 的次數映射數組就可以通過 cnt[j+1] - cnt[i] 來表示,但數組之間不好直接做減法,可以再進一步訪問每個字母來分別進行處理,快速得到每個字母的出現次數后除以2,將結果累加到 sum 中,就是出現奇數次字母的個數了,再除以2和k比較即可,參見代碼如下:


class Solution {
public:
    vector<bool> canMakePaliQueries(string s, vector<vector<int>>& queries) {
        vector<bool> res;
        vector<vector<int>> cnt(s.size() + 1, vector<int>(26));
        for (int i = 0; i < s.size(); ++i) {
            cnt[i + 1] = cnt[i];
            ++cnt[i + 1][s[i] - 'a'];
        }
        for (auto &query : queries) {
            int sum = 0;
            for (int i = 0; i < 26; ++i) {
                sum += (cnt[query[1] + 1][i] - cnt[query[0]][i]) % 2;
            }
            res.push_back(sum / 2 <= query[2]);
        }
        return res;
    }
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/1177


參考資料:

https://leetcode.com/problems/can-make-palindrome-from-substring/

https://leetcode.com/problems/can-make-palindrome-from-substring/discuss/372044/Short-and-fast-C%2B%2B-prefix-xor-solution-beats-100

https://leetcode.com/problems/can-make-palindrome-from-substring/discuss/371849/JavaPython-3-3-codes-each%3A-prefix-sum-boolean-and-xor-of-characters'-frequencies-then-compare


LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM