Welcome to Subscribe On Youtube

4053. Minimum Operations to Make Every Element Palindromic

Description

You are given an integer array nums.

In one operation, you may choose an index i and either increment or decrement nums[i] by 2.

Return the minimum number of operations required to make every element in nums a positive palindrome. Different elements may be changed into different palindromic integers.

 

Example 1:

Input: nums = [10,12,14,16]

Output: 9

Explanation:

One optimal sequence of operations is:

  • Decrement nums[0] by 2 once to change it from 10 to 8.
  • Decrement nums[1] by 2 twice to change it from 12 to 8.
  • Decrement nums[2] by 2 three times to change it from 14 to 8.
  • Increment nums[3] by 2 three times to change it from 16 to 22.

After 1 + 2 + 3 + 3 = 9 operations, nums = [8, 8, 8, 22], and every element is a positive palindromic integer.

It can be shown that fewer than 9 operations cannot achieve this.

Example 2:

Input: nums = [9,10,11,10]

Output: 2

Explanation:

Decrement nums[1] and nums[3] by 2 once each.

After 2 operations, nums = [9, 8, 11, 8], and every element is a positive palindromic integer.

At least one operation is needed for each of these two elements, so the minimum number of operations is 2.

Example 3:

Input: nums = [125]

Output: 2

Explanation:

Decrement nums[0] by 2 twice to change it from 125 to 121, which is a positive palindromic integer.

A single operation would change it to 123 or 127, neither of which is palindromic. Thus, the minimum number of operations is 2.

 

Constraints:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109

Solutions

Thinking

Each operation adds or subtracts $2$, so parity never changes: $\textit{nums}[i]$ can only become a positive palindrome of the same parity. The elements are independent, and the answer is the sum of each value’s distance to the nearest same-parity palindrome, divided by $2$.

With $n = 10^5$ and values up to $10^9$, walking from $x$ by steps of $2$ until a palindrome appears is too slow.

Every palindrome is a mirrored prefix. Enumerating prefixes $1 \ldots 10^5$ and forming both even-length and odd-length palindromes covers everything around $10^9$. Split them by parity, sort each list, and binary-search the nearest neighbor for every $x$.

An operation increments or decrements an element by $2$, so its parity is invariant and the target palindrome must have the same parity. The elements are independent: for each $x$, find the nearest same-parity positive palindrome $p$ and add $\lvert x - p \rvert / 2$.

During preprocessing, enumerate prefixes $i = 1, 2, \ldots, 10^5$ and let $s$ be the decimal representation of $i$:

  • Even-length palindrome: $s + \mathrm{reverse}(s)$
  • Odd-length palindrome: $s + \mathrm{reverse}(s[:-1])$

Store them in two lists by parity and sort each list. This range covers all palindromes with up to about $12$ digits, which is enough for values up to $10^9$.

For each $x$, binary-search the first palindrome that is at least $x$ in the same-parity list, compare it with the previous one, and take the smaller distance divided by $2$.

Let $M$ be the number of palindromes (about $2 \times 10^5$). Preprocessing takes $O(M \log M)$ and each query takes $O(\log M)$. The overall time complexity is $O(M \log M + n \log M)$, and the space complexity is $O(M)$.

  • class Solution {
        static List<Long>[] ps = new ArrayList[2];
    
        static {
            ps[0] = new ArrayList<>();
            ps[1] = new ArrayList<>();
            for (int i = 1; i <= 100000; i++) {
                String s = String.valueOf(i);
                String t1 = new StringBuilder(s).reverse().toString();
                String t2 = new StringBuilder(s.substring(0, s.length() - 1)).reverse().toString();
                long x = Long.parseLong(s + t1);
                ps[(int) (x & 1)].add(x);
                long y = Long.parseLong(s + t2);
                ps[(int) (y & 1)].add(y);
            }
            ps[0].sort(Long::compare);
            ps[1].sort(Long::compare);
        }
    
        public long minOperations(int[] nums) {
            long ans = 0;
            for (int x : nums) {
                List<Long> p = ps[x & 1];
                int l = 0, r = p.size();
                while (l < r) {
                    int m = (l + r) >>> 1;
                    if (p.get(m) < x) {
                        l = m + 1;
                    } else {
                        r = m;
                    }
                }
                long t = Long.MAX_VALUE;
                if (l < p.size()) {
                    t = p.get(l) - x;
                }
                if (l > 0) {
                    t = Math.min(t, (long) x - p.get(l - 1));
                }
                ans += t / 2;
            }
            return ans;
        }
    }
    
    
  • class Solution {
    public:
        long long minOperations(vector<int>& nums) {
            static vector<long long> ps[2];
            if (ps[0].empty()) {
                for (long long i = 1; i <= 100000; ++i) {
                    string s = to_string(i);
                    string t1 = s;
                    reverse(t1.begin(), t1.end());
                    string t2 = s.substr(0, s.size() - 1);
                    reverse(t2.begin(), t2.end());
                    long long x = stoll(s + t1);
                    ps[x & 1].push_back(x);
                    long long y = stoll(s + t2);
                    ps[y & 1].push_back(y);
                }
                sort(ps[0].begin(), ps[0].end());
                sort(ps[1].begin(), ps[1].end());
            }
            long long ans = 0;
            for (int x : nums) {
                auto& p = ps[x & 1];
                auto it = lower_bound(p.begin(), p.end(), x);
                long long t = LLONG_MAX;
                if (it != p.end()) {
                    t = *it - x;
                }
                if (it != p.begin()) {
                    t = min(t, (long long) x - *prev(it));
                }
                ans += t / 2;
            }
            return ans;
        }
    };
    
    
  • ps = [[], []]
    for i in range(1, 10**5 + 1):
        s = str(i)
        t1 = s[::-1]
        t2 = s[:-1][::-1]
        x = int(s + t1)
        ps[x & 1].append(x)
        y = int(s + t2)
        ps[y & 1].append(y)
    for p in ps:
        p.sort()
    
    
    class Solution:
        def minOperations(self, nums: list[int]) -> int:
            ans = 0
            for x in nums:
                p = ps[x & 1]
                i = bisect_left(p, x)
                t = inf
                if i < len(p):
                    t = p[i] - x
                if i:
                    t = min(t, x - p[i - 1])
                ans += t // 2
            return ans
    
    
  • var ps [2][]int64
    
    func init() {
    	for i := int64(1); i <= 100000; i++ {
    		s := strconv.FormatInt(i, 10)
    		t1 := reverse(s)
    		t2 := reverse(s[:len(s)-1])
    		x, _ := strconv.ParseInt(s+t1, 10, 64)
    		ps[x&1] = append(ps[x&1], x)
    		y, _ := strconv.ParseInt(s+t2, 10, 64)
    		ps[y&1] = append(ps[y&1], y)
    	}
    	sort.Slice(ps[0], func(a, b int) bool { return ps[0][a] < ps[0][b] })
    	sort.Slice(ps[1], func(a, b int) bool { return ps[1][a] < ps[1][b] })
    }
    
    func reverse(s string) string {
    	b := []byte(s)
    	for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
    		b[i], b[j] = b[j], b[i]
    	}
    	return string(b)
    }
    
    func minOperations(nums []int) int64 {
    	var ans int64
    	for _, x := range nums {
    		p := ps[x&1]
    		i := sort.Search(len(p), func(i int) bool { return p[i] >= int64(x) })
    		t := int64(1 << 62)
    		if i < len(p) {
    			t = p[i] - int64(x)
    		}
    		if i > 0 {
    			t = min(t, int64(x)-p[i-1])
    		}
    		ans += t / 2
    	}
    	return ans
    }
    
    
  • const ps: number[][] = [[], []];
    
    for (let i = 1; i <= 10 ** 5; i++) {
        const s = String(i);
        const t1 = [...s].reverse().join('');
        const t2 = [...s.slice(0, -1)].reverse().join('');
        const x = Number(s + t1);
        ps[x & 1].push(x);
        const y = Number(s + t2);
        ps[y & 1].push(y);
    }
    ps[0].sort((a, b) => a - b);
    ps[1].sort((a, b) => a - b);
    
    function minOperations(nums: number[]): number {
        let ans = 0;
        for (const x of nums) {
            const p = ps[x & 1];
            let l = 0;
            let r = p.length;
            while (l < r) {
                const m = (l + r) >> 1;
                if (p[m] < x) {
                    l = m + 1;
                } else {
                    r = m;
                }
            }
            let t = Number.MAX_SAFE_INTEGER;
            if (l < p.length) {
                t = p[l] - x;
            }
            if (l > 0) {
                t = Math.min(t, x - p[l - 1]);
            }
            ans += Math.floor(t / 2);
        }
        return ans;
    }
    
    

All Problems

All Solutions