Problem:
Score : 600 points

Problem Statement

We have N strings of lowercase English letters: S_1, S_2, \cdots, S_N.

Takahashi wants to make a string that is a palindrome by choosing one or more of these strings - the same string can be chosen more than once - and concatenating them in some order of his choice.

The cost of using the string S_i once is C_i, and the cost of using it multiple times is C_i multiplied by that number of times.

Find the minimum total cost needed to choose strings so that Takahashi can make a palindrome.

If there is no choice of strings in which he can make a palindrome, print -1.

Constraints

1 \leq N \leq 50

1 \leq |S_i| \leq 20

S_i consists of lowercase English letters.

1 \leq C_i \leq 10^9

Input

Input is given from Standard Input in the following format:

N
S_1 C_1
S_2 C_2
:
S_N C_N

Output

Print the minimum total cost needed to choose strings so that Takahashi can make a palindrome, or -1 if there is no such choice.

Sample Input 1

3
ba 3
abc 4
cbaa 5

Sample Output 1

7

We have ba, abc, and cbaa.

For example, we can use ba once and abc once for a cost of 7, then concatenate them in the order abc, ba to make a palindrome.
Also, we can use abc once and cbaa once for a cost of 9, then concatenate them in the order cbaa, abc to make a palindrome.

We cannot make a palindrome for a cost less than 7, so we should print 7.

Sample Input 2

2
abcab 5
cba 3

Sample Output 2

11

We can choose abcab once and cba twice, then concatenate them in the order abcab, cba, cba to make a palindrome for a cost of 11.

Sample Input 3

4
ab 5
cba 3
a 12
ab 10

Sample Output 3

8

We can choose a once, which is already a palindrome, but it is cheaper to concatenate ab and cba.

Sample Input 4

2
abc 1
ab 2

Sample Output 4

-1

We cannot make a palindrome, so we should print -1.

Solution in Java (OpenJDK 11.0.6) (judge verdict: Runtime Error)


import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;

import java.util.*;

public class Main {
    long MOD = (long) 1e9 + 7;

    InputStream in;
    OutputStream out;

    public Main() {
        this(System.in, System.out);
    }

    public Main(InputStream in, OutputStream out) {
        this.in = in;
        this.out = out;
    }

    int n;
    String[] s, sr;
    long[] c;

    void run() {
        Scanner sc = new Scanner(this.in);
        PrintWriter out = new PrintWriter(this.out);

        // write your code
        n = sc.nextInt();
        s = new String[n];
        sr = new String[n];
        c = new long[n];
        for (int i = 0; i < n; i++) {
            s[i] = sc.next();
            sr[i] = reverse(s[i]);
            c[i] = sc.nextInt();
        }

        out.println(dijkstra());

        out.flush();
    }

    long INF = 1L << 61;

    long dijkstra() {

        HashMap<String, Long> memo = new HashMap<>();
        PriorityQueue<Data> q = new PriorityQueue<>();

        for (int i = 0; i < n; i++) {
            if (memo.getOrDefault(s[i], INF) > c[i]) {
                q.add(new Data(s[i], c[i], true));
                memo.put(s[i], c[i]);
            }
        }

        debug(memo);

        while (!q.isEmpty()) {
            Data cur = q.remove();
//            debug(cur.s, cur.c, cur.rev);

            if (memo.get(cur.s) != cur.c) continue;
            if (ok(cur.s)) {
                return cur.c;
            }

            for (int i = 0; i < n; i++) {
                String target = cur.rev ? sr[i] : s[i];
                int nlen = target.length(), clen = cur.s.length();
                if (target.startsWith(cur.s)) {
                    String next = target.substring(clen);
                    if (memo.getOrDefault(next, INF) > cur.c + c[i]) {
                        Data d = new Data(
                                next,
                                cur.c + c[i],
                                !cur.rev
                        );
                        q.add(d);
                        memo.put(d.s, d.c);
                    }
                } else if (cur.s.startsWith(target)) {
                    String next = cur.s.substring(nlen);
                    if (memo.getOrDefault(next, INF) > cur.c + c[i]) {
                        Data d = new Data(
                                next,
                                cur.c + c[i],
                                cur.rev
                        );
                        q.add(d);
                        memo.put(d.s, d.c);
                    }
                }
            }
        }

        return -1;
    }

    String reverse(String s) {
        StringBuilder sb = new StringBuilder();
        int l = s.length();
        for (int i = 0; i < l; i++) {
            sb.append(s.charAt(l - i - 1));
        }
        return sb.toString();
    }
    
    boolean ok(String s) {
        int l = s.length();
        for (int i = 0; i < l; i++) {
            if (s.charAt(i) != s.charAt(n - i - 1)) return false;
        }
        return true;
    } 


    class Data implements Comparable<Data> {

        long c;
        String s;
        boolean rev;

        Data(String s, long c, boolean rev) {
            this.s = s;
            this.c = c;
            this.rev = rev;
        }

        @Override
        public int compareTo(Data o) {
            return Long.compare(c, o.c);
        }
    }

    void debug(Object... os) {
        System.err.println(Arrays.deepToString(os));
    }

    public static void main(String[] args) {
        new Main().run();
    }
}