Problem

Given a string s containing lowercase English letters and the character ?.

We have to replace every ? with a lowercase English letter so that the final string becomes a palindrome.

Among all possible palindromes, we need to find the one having the minimum sum of absolute ASCII differences between adjacent characters.

If it is impossible to make the string a palindrome, return -1.

Example

Code
Input:
s = "a???c??c????"

Output:
4

One possible palindrome is:

Code
abbbccccbbba

The adjacent differences are:

Code
|a-b| = 1
|b-b| = 0
|b-b| = 0
|b-c| = 1
|c-c| = 0
|c-c| = 0
|c-c| = 0
|c-b| = 1
|b-b| = 0
|b-b| = 0
|b-a| = 1

Total:

Code
1 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + 1 = 4

Understanding the Palindrome Property

A palindrome reads the same from left to right and right to left.

For example:

Code
abcba

The matching positions are:

CSS
a ↔ a
b ↔ b
c ↔ c
b ↔ b
a ↔ a

For a string of length n, position i must be equal to:

Code
n - 1 - i

Therefore, for every pair:

Code
s[i] and s[n - 1 - i]

we have three possibilities.

Case 1: Both are fixed characters

Example:

Code
a ... a

This is valid because both characters are already equal.

But:

Code
a ... c

is impossible because neither character can be changed.

So we return:

Code
-1

Case 2: One character is ?

Example:

Code
a ... ?

The ? must become:

Code
a

Similarly:

Code
? ... c

must become:

Code
c

So there is only one possible choice.


Case 3: Both characters are ?

Example:

Code
? ... ?

Both characters must become the same character.

We have complete freedom to choose that character.

To minimize the adjacent difference, we can choose a character that continues the previous fixed character.

For example:

Code
a ? ? c

can become:

Code
a a a c

instead of unnecessarily introducing another character.

Therefore, the ? region does not create an additional cost between fixed characters.


Important Observation

Instead of constructing the entire palindrome, we can process only the left half of the string.

Why?

Consider:

Code
a b c c b a

The adjacent differences are:

CSS
a-b
b-c
c-c
c-b
b-a

The right half contains the same differences in reverse order.

Therefore:

Code
Total cost = 2 × cost of the left half

So we only need to find the minimum cost between consecutive characters in the left half.


Another Important Observation

Suppose the fixed characters that appear while scanning the left half are:

Code
a → c → f

The minimum cost is:

Code
|a-c| + |c-f|

The ? characters between them can simply be replaced with the previous character.

For example:

Code
a ? ? c ? ? f

can become:

Code
a a a c c c f

The cost becomes:

Code
|a-a| + |a-a| + |a-c| + |c-c| + |c-c| + |c-f|

Only the transitions between fixed characters contribute:

Code
|a-c| + |c-f|

Since the same transitions appear on the other side of the palindrome, multiply by 2.


Algorithm

We maintain a variable called previous.

It represents the previous fixed character that we encountered in the left half.

For every mirrored pair:

Code
left  = s[i]
right = s[n - 1 - i]

we do the following:

Step 1: Check if both are fixed

If:

Code
left != '?' && right != '?'

then they must be equal.

If:

Code
left != right

return:

Code
-1

Otherwise, this pair represents that character.


Step 2: If only one is fixed

If one side is ?, the other character determines what it must become.

For example:

Code
a ?

becomes:

Code
a a

So we use a.


Step 3: If both are ?

There is no fixed character that needs to be added.

We can continue without adding a cost.


Step 4: Calculate the difference

When we find a new fixed character:

Code
current

and already have:

Code
previous

the cost between them is:

Code
Math.abs(current - previous)

Because the same transition appears on the other half:

Code
answer += 2 * Math.abs(current - previous);

Java Implementation
C#
class Solution {
    public int minimumSum(String s) {

        int n = s.length();
        int answer = 0;

        char previous = 0;
        boolean hasPrevious = false;

        // Process only the left half
        for (int i = 0; i < n / 2; i++) {

            char left = s.charAt(i);
            char right = s.charAt(n - 1 - i);

            char current;

            // Both characters are fixed
            if (left != '?' && right != '?') {

                // Different characters cannot be changed
                if (left != right) {
                    return -1;
                }

                current = left;
            }

            // Left character is fixed
            else if (left != '?') {
                current = left;
            }

            // Right character is fixed
            else if (right != '?') {
                current = right;
            }

            // Both characters are '?'
            else {
                continue;
            }

            // Calculate difference from previous fixed character
            if (hasPrevious) {
                answer += 2 * Math.abs(current - previous);
            }

            previous = current;
            hasPrevious = true;
        }

        return answer;
    }
}
Code Explanation

Let's understand the important parts of the code.

1. Store the length

Code
int n = s.length();

We need the length to find the mirrored character.

For index i, its corresponding character is:

Code
n - 1 - i

2. Store the answer

Code
int answer = 0;

This stores the minimum total adjacent difference.


3. Store the previous fixed character

Code
char previous = 0;
boolean hasPrevious = false;

previous stores the previous character that is fixed or determined.

For example:

Code
a ? ? c ? f

while scanning, we get:

Code
a → c → f

So:

Code
previous = a
previous = c
previous = f

4. Process only half the string

Code
for (int i = 0; i < n / 2; i++)

We don't need to process the complete string because the right half is the mirror image.

This also makes the solution efficient.


5. Find the mirrored characters

Code
char left = s.charAt(i);
char right = s.charAt(n - 1 - i);

For example:

Code
s = "abcba"

When:

Code
i = 0

we get:

Code
left = 'a'
right = 'a'

When:

Code
i = 1

we get:

Code
left = 'b'
right = 'b'

Handling Fixed Characters
Code
if (left != '?' && right != '?') {

Both characters are known.

For example:

Code
a ... a

is valid.

But:

Code
a ... c

is impossible.

Therefore:

Code
if (left != right) {
    return -1;
}

Handling One ?
Code
else if (left != '?') {
    current = left;
}

If the left character is fixed, the right ? must become the same character.

For example:

Code
a ... ?

becomes:

Code
a ... a

Similarly:

Code
else if (right != '?') {
    current = right;
}

handles:

Code
? ... c

which becomes:

Code
c ... c

Handling Two ?
Code
else {
    continue;
}

When both characters are ?, we don't have a fixed character to process.

We can choose a suitable character later without increasing the minimum cost.


Calculating the Cost

This is the most important line:

Code
answer += 2 * Math.abs(current - previous);

Suppose:

Code
previous = 'a'
current = 'c'

ASCII values are:

Code
'a' = 97
'c' = 99

Therefore:

Code
|97 - 99| = 2

The same transition appears on the other side of the palindrome.

So:

Code
2 × 2 = 4

is added to the answer.


Dry Run

Consider:

Code
s = "a???c??c????"

We process the mirrored pairs.

The important fixed characters encountered from the left half are:

Code
a
c

The transition is:

Code
a → c

ASCII difference:

Code
|97 - 99| = 2

Because it appears on both sides:

Code
2 × 2 = 4

Therefore:

Code
answer = 4

Why This Greedy Approach Works

The important point is that a sequence of ? characters between two fixed characters does not force us to create additional cost.

Suppose:

Code
a ? ? ? c

We can choose:

Code
a a a a c

The cost is:

Code
|a-a| + |a-a| + |a-a| + |a-c|

which is simply:

Code
0 + 0 + 0 + |a-c|

So there is no benefit in choosing intermediate characters such as:

Code
a b c d c

because that would create unnecessary differences.

Thus, we only need to consider the transitions between consecutive fixed characters.


Complexity Analysis

Let n be the length of the string.

Time Complexity

Code
O(n)

We scan approximately half of the string.

Since:

Code
n / 2 = O(n)

the overall complexity is O(n).

Auxiliary Space

Code
O(1)

We only use a few variables:

Code
answer
previous
hasPrevious
left
right
current

No additional array or string is required.


Key Takeaways

The main ideas used in this problem are:

  1. Palindrome property

    Code
    s[i] == s[n-1-i]
  2. Check mirrored characters

    • Same fixed characters → valid.

    • Different fixed characters → impossible.

    • ? + fixed → replace ? with the fixed character.

    • ? + ? → choose a suitable character.

  3. Process only half of the string.

  4. Ignore consecutive ? regions when calculating cost, because they can be filled with the previous character.

  5. Every transition occurs twice in a palindrome, so:

    Code
    total cost = 2 × left-half cost
  6. The final solution runs in:

    Code
    Time: O(n)
    Space: O(1)

Pattern to Remember

When you see a problem involving:

Code
Palindrome + '?' + minimum cost

first think about the mirrored pairs:

Code
i  ↔  n - 1 - i

Then think about the fixed characters and the cost between consecutive fixed characters. This reduces the problem from constructing many possible strings to a simple linear scan.