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 Rust (1.42.0) (judge verdict: Accepted)

#[allow(unused_imports)]
use std::cmp::*;
#[allow(unused_imports)]
use std::collections::*;
use std::io::{Write, BufWriter};
// https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8
macro_rules! input {
    ($($r:tt)*) => {
        let stdin = std::io::stdin();
        let mut bytes = std::io::Read::bytes(std::io::BufReader::new(stdin.lock()));
        let mut next = move || -> String{
            bytes
                .by_ref()
                .map(|r|r.unwrap() as char)
                .skip_while(|c|c.is_whitespace())
                .take_while(|c|!c.is_whitespace())
                .collect()
        };
        input_inner!{next, $($r)*}
    };
}

macro_rules! input_inner {
    ($next:expr) => {};
    ($next:expr, ) => {};
    ($next:expr, $var:ident : $t:tt $($r:tt)*) => {
        let $var = read_value!($next, $t);
        input_inner!{$next $($r)*}
    };
}

macro_rules! read_value {
    ($next:expr, [graph1; $len:expr]) => {{
        let mut g = vec![vec![]; $len];
        let ab = read_value!($next, [(usize1, usize1)]);
        for (a, b) in ab {
            g[a].push(b);
            g[b].push(a);
        }
        g
    }};
    ($next:expr, ( $($t:tt),* )) => {
        ( $(read_value!($next, $t)),* )
    };
    ($next:expr, [ $t:tt ; $len:expr ]) => {
        (0..$len).map(|_| read_value!($next, $t)).collect::<Vec<_>>()
    };
    ($next:expr, chars) => {
        read_value!($next, String).chars().collect::<Vec<char>>()
    };
    ($next:expr, usize1) => (read_value!($next, usize) - 1);
    ($next:expr, [ $t:tt ]) => {{
        let len = read_value!($next, usize);
        read_value!($next, [$t; len])
    }};
    ($next:expr, $t:ty) => ($next().parse::<$t>().expect("Parse error"));
}

#[allow(unused)]
macro_rules! debug {
    ($($format:tt)*) => (write!(std::io::stderr(), $($format)*).unwrap());
}
#[allow(unused)]
macro_rules! debugln {
    ($($format:tt)*) => (writeln!(std::io::stderr(), $($format)*).unwrap());
}

/*
 * Dijkstra's algorithm.
 * Verified by: AtCoder ABC164 (https://atcoder.jp/contests/abc164/submissions/12423853)
 */

struct Dijkstra {
    edges: Vec<Vec<(usize, i64)>>, // adjacent list representation
}

impl Dijkstra {
    fn new(n: usize) -> Self {
        Dijkstra { edges: vec![Vec::new(); n] }
    }
    fn add_edge(&mut self, from: usize, to: usize, cost: i64) {
        self.edges[from].push((to, cost));
    }
    /*
     * This function returns a Vec consisting of the distances from vertex source.
     */
    fn solve(&self, source: usize, inf: i64) -> Vec<i64> {
        let n = self.edges.len();
        let mut d = vec![inf; n];
        // que holds (-distance, vertex), so that que.pop() returns the nearest element.
        let mut que = std::collections::BinaryHeap::new();
        que.push((0, source));
        while let Some((cost, pos)) = que.pop() {
            let cost = -cost;
            if d[pos] <= cost {
                continue;
            }
            d[pos] = cost;
            for &(w, c) in &self.edges[pos] {
                let newcost = cost + c;
                if d[w] > newcost {
                    d[w] = newcost + 1;
                    que.push((-newcost, w));
                }
            }
        }
        return d;
    }
}

fn is_pal(a: &[char]) -> bool {
    let n = a.len();
    for i in 0..n / 2 {
        if a[i] != a[n - 1 - i] {
            return false;
        }
    }
    true
}

fn solve() {
    let out = std::io::stdout();
    let mut out = BufWriter::new(out.lock());
    macro_rules! puts {
        ($($format:tt)*) => (let _ = write!(out,$($format)*););
    }
    input! {
        n: usize,
        sc: [(chars, i64); n],
    }
    let mut srevc = vec![];
    let mut st = vec![];
    let mut strev = vec![];
    st.push(vec![]);
    strev.push(vec![]);
    for &(ref s, c) in &sc {
        let l = s.len();
        for i in 0..l {
            st.push(s[i..].to_vec());
        }
        let mut t = s.clone();
        t.reverse();
        for i in 0..l {
            strev.push(t[i..].to_vec());
        }
        srevc.push((t, c));
    }
    st.sort(); st.dedup();
    strev.sort(); strev.dedup();
    let m1 = st.len();
    let m2 = strev.len();
    let mut dijk = Dijkstra::new(m1 + m2 + 1);
    for i in 0..m1 {
        let s = &st[i];
        for &(ref t, c) in &srevc {
            let l = min(s.len(), t.len());
            if s[..l] != t[..l] {
                continue;
            }
            if s.len() > l {
                // e.g. s = "abcd", t = "abc"
                let nxt = s[l..].to_vec();
                let idx = st.binary_search(&nxt).unwrap();
                dijk.add_edge(i, idx, c);
            } else {
                // e.g. s = "abc", t = "abcde"
                let nxt = t[l..].to_vec();
                let idx = strev.binary_search(&nxt).unwrap();
                dijk.add_edge(i, m1 + idx, c);
            }
        }
    }
    for i in 0..m2 {
        let s = &strev[i];
        for &(ref t, c) in &sc {
            let l = min(s.len(), t.len());
            if s[..l] != t[..l] {
                continue;
            }
            if s.len() > l {
                // e.g. s = "abcd", t = "abc"
                let nxt = s[l..].to_vec();
                let idx = strev.binary_search(&nxt).unwrap();
                dijk.add_edge(m1 + i, m1 + idx, c);
            } else {
                // e.g. s = "abc", t = "abcde"
                let nxt = t[l..].to_vec();
                let idx = st.binary_search(&nxt).unwrap();
                dijk.add_edge(m1 + i, idx, c);
            }
        }
    }
    for &(ref s, c) in &sc {
        let idx = st.binary_search(s).unwrap();
        dijk.add_edge(m1 + m2, idx, c);
    }
    for &(ref t, c) in &srevc {
        let idx = strev.binary_search(t).unwrap();
        dijk.add_edge(m1 + m2, m1 + idx, c);
    }
    const INF: i64 = 1 << 55;
    let sol = dijk.solve(m1 + m2, INF);
    let mut mi = INF;
    for i in 0..m1 {
        if is_pal(&st[i]) {
            mi = min(mi, sol[i]);
        }
    }
    for i in 0..m2 {
        if is_pal(&strev[i]) {
            mi = min(mi, sol[m1 + i]);
        }
    }
    puts!("{}\n", if mi >= INF { -1 } else { mi });
}

fn main() {
    // In order to avoid potential stack overflow, spawn a new thread.
    let stack_size = 104_857_600; // 100 MB
    let thd = std::thread::Builder::new().stack_size(stack_size);
    thd.spawn(|| solve()).unwrap().join().unwrap();
}