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)

use std::cmp::{min, Reverse};
use std::collections::{BTreeMap, BinaryHeap};

const INF: i64 = 1 << 60;

fn main() {
    let (r, w) = (std::io::stdin(), std::io::stdout());
    let mut sc = IO::new(r.lock(), w.lock());
    let n: usize = sc.read();
    let mut t = vec![];
    for _ in 0..n {
        let s = sc.chars();
        let cost: i64 = sc.read();
        t.push((s, cost));
    }

    let mut dist = BTreeMap::new();
    let mut heap = BinaryHeap::new();
    for (s, cost) in t.iter() {
        let cur = dist.entry((s.clone(), true)).or_insert(INF);
        if *cur > *cost {
            *cur = *cost;
            heap.push((Reverse(*cost), s.clone(), true));
        }
    }

    let mut ans = INF;
    while let Some((_, s, is_head)) = heap.pop() {
        let cur_cost = dist[&(s.clone(), is_head)];

        if is_palindrome(&s) {
            ans = min(ans, cur_cost);
        }

        for (edge, cost) in t.iter() {
            let next_cost = cur_cost + cost;
            if is_head {
                let tail_length = match_length(&s, &edge);
                if s.len() == tail_length && edge.len() == tail_length {
                    let next = vec![];
                    let cur = dist.entry((next.clone(), true)).or_insert(INF);
                    if *cur > next_cost {
                        *cur = next_cost;
                        heap.push((Reverse(next_cost), next.clone(), true));
                    }
                    let cur = dist.entry((next.clone(), false)).or_insert(INF);
                    if *cur > next_cost {
                        *cur = next_cost;
                        heap.push((Reverse(next_cost), next.clone(), false));
                    }
                } else if tail_length == s.len() {
                    let next = edge[..(edge.len() - tail_length)].to_vec();
                    let next_forward = false;
                    let cur = dist.entry((next.clone(), next_forward)).or_insert(INF);
                    if *cur > next_cost {
                        *cur = next_cost;
                        heap.push((Reverse(next_cost), next.clone(), next_forward));
                    }
                } else if tail_length == edge.len() {
                    let next = s[tail_length..].to_vec();
                    let next_forward = true;
                    let cur = dist.entry((next.clone(), next_forward)).or_insert(INF);
                    if *cur > next_cost {
                        *cur = next_cost;
                        heap.push((Reverse(next_cost), next.clone(), next_forward));
                    }
                }
            } else {
                let head_length = match_length(&edge, &s);
                if head_length == edge.len() && head_length == s.len() {
                    let next = vec![];
                    let cur = dist.entry((next.clone(), true)).or_insert(INF);
                    if *cur > next_cost {
                        *cur = next_cost;
                        heap.push((Reverse(next_cost), next.clone(), true));
                    }
                    let cur = dist.entry((next.clone(), false)).or_insert(INF);
                    if *cur > next_cost {
                        *cur = next_cost;
                        heap.push((Reverse(next_cost), next.clone(), false));
                    }
                } else if head_length == edge.len() {
                    let next = s[..(s.len() - head_length)].to_vec();
                    let next_forward = false;
                    let cur = dist.entry((next.clone(), next_forward)).or_insert(INF);
                    if *cur > next_cost {
                        *cur = next_cost;
                        heap.push((Reverse(next_cost), next.clone(), next_forward));
                    }
                } else if head_length == s.len() {
                    let next = edge[head_length..].to_vec();
                    let next_forward = true;
                    let cur = dist.entry((next.clone(), next_forward)).or_insert(INF);
                    if *cur > next_cost {
                        *cur = next_cost;
                        heap.push((Reverse(next_cost), next.clone(), next_forward));
                    }
                }
            }
        }
    }

    if ans == INF {
        println!("-1");
    } else {
        println!("{}", ans);
    }
}

fn match_length<T: PartialEq>(head: &[T], tail: &[T]) -> usize {
    for i in 0.. {
        if i >= head.len() || i >= tail.len() || head[i] != tail[tail.len() - 1 - i] {
            return i;
        }
    }
    unimplemented!()
}
fn is_palindrome<T: PartialEq>(s: &[T]) -> bool {
    let n = s.len();
    for i in 0..n {
        if s[i] != s[n - 1 - i] {
            return false;
        }
    }
    true
}

pub struct IO<R, W: std::io::Write>(R, std::io::BufWriter<W>);

impl<R: std::io::Read, W: std::io::Write> IO<R, W> {
    pub fn new(r: R, w: W) -> Self {
        Self(r, std::io::BufWriter::new(w))
    }
    pub fn write<S: ToString>(&mut self, s: S) {
        use std::io::Write;
        self.1.write_all(s.to_string().as_bytes()).unwrap();
    }
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .0
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n' || b == b'\r' || b == b'\t')
            .take_while(|&b| b != b' ' && b != b'\n' && b != b'\r' && b != b'\t')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}