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()
    }
}


Measured memory_bytes: 2440