package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"time"
)
type Word struct {
c int
seq []int
}
func scorePermutation(perm []int, words []Word) int64 {
pos := make([]int, 12)
for i, v := range perm {
pos[v] = i
}
var total int64 = 0
for _, w := range words {
ok := true
for i := 0; i+1 < len(w.seq); i++ {
if abs(pos[w.seq[i]]-pos[w.seq[i+1]]) != 1 {
ok = false
break
}
}
if ok {
total += int64(w.c)
}
}
return total
}
func hillClimb(perm []int, words []Word) (bestPerm []int, bestScore int64) {
bestPerm = make([]int, 12)
copy(bestPerm, perm)
bestScore = scorePermutation(bestPerm, words)
improved := true
tmp := make([]int, 12)
for improved {
improved = false
localBestDelta := int64(0)
swapI, swapJ := -1, -1
for i := 0; i < 12; i++ {
for j := i + 1; j < 12; j++ {
copy(tmp, bestPerm)
tmp[i], tmp[j] = tmp[j], tmp[i]
s := scorePermutation(tmp, words)
delta := s - bestScore
if delta > localBestDelta {
localBestDelta = delta
swapI, swapJ = i, j
}
}
}
if localBestDelta > 0 && swapI >= 0 {
bestPerm[swapI], bestPerm[swapJ] = bestPerm[swapJ], bestPerm[swapI]
bestScore += localBestDelta
improved = true
}
}
return bestPerm, bestScore
}
func randomPermutation() []int {
p := make([]int, 12)
for i := 0; i < 12; i++ {
p[i] = i
}
for i := 11; i > 0; i-- {
j := rand.Intn(i + 1)
p[i], p[j] = p[j], p[i]
}
return p
}
func perturb(p []int) {
// random small perturbation: either swap, reverse segment or insert
n := len(p)
t := rand.Intn(3)
switch t {
case 0:
i := rand.Intn(n)
j := rand.Intn(n)
if i != j {
p[i], p[j] = p[j], p[i]
}
case 1:
i := rand.Intn(n)
j := rand.Intn(n)
if i > j {
i, j = j, i
}
for i < j {
p[i], p[j] = p[j], p[i]
i++
j--
}
case 2:
i := rand.Intn(n)
j := rand.Intn(n)
if i == j {
return
}
val := p[i]
if i < j {
copy(p[i:], p[i+1:j+1])
p[j] = val
} else {
copy(p[j+1:], p[j:i])
p[j] = val
}
}
}
func main() {
in := bufio.NewReader(os.Stdin)
var n int
if _, err := fmt.Fscan(in, &n); err != nil {
return
}
words := make([]Word, 0, n)
for i := 0; i < n; i++ {
var c int
var s string
fmt.Fscan(in, &c, &s)
seq := make([]int, len(s))
ok := true
for j, ch := range []byte(s) {
if ch < 'a' || ch > 'l' {
ok = false
break
}
seq[j] = int(ch - 'a')
if j > 0 && seq[j] == seq[j-1] {
ok = false
break
}
}
if !ok || len(seq) < 2 {
continue
}
words = append(words, Word{c: c, seq: seq})
}
rand.Seed(time.Now().UnixNano())
// Initial permutation heuristic: frequency-based ordering
freq := make([]int64, 12)
for _, w := range words {
for i := 0; i+1 < len(w.seq); i++ {
a, b := w.seq[i], w.seq[i+1]
freq[a] += int64(w.c)
freq[b] += int64(w.c)
}
}
init := make([]int, 12)
used := make([]bool, 12)
for k := 0; k < 12; k++ {
best := -1
var bestF int64 = -1
for x := 0; x < 12; x++ {
if !used[x] && freq[x] > bestF {
bestF = freq[x]
best = x
}
}
if best == -1 {
for x := 0; x < 12; x++ {
if !used[x] {
best = x
break
}
}
}
init[k] = best
used[best] = true
}
bestPerm, bestScore := hillClimb(init, words)
// Time-bounded multi-start with perturbations and restarts
start := time.Now()
timeLimit := 1200 * time.Millisecond
// Attempt several phases: local perturbations around current best, plus random restarts
for time.Since(start) < timeLimit {
// Local perturbations from best
cur := make([]int, 12)
copy(cur, bestPerm)
for t := 0; t < 50 && time.Since(start) < timeLimit; t++ {
perturb(cur)
cur2, sc := hillClimb(cur, words)
if sc > bestScore {
bestScore = sc
copy(bestPerm, cur2)
copy(cur, cur2)
}
}
// Random restarts
for r := 0; r < 5 && time.Since(start) < timeLimit; r++ {
p := randomPermutation()
p2, sc := hillClimb(p, words)
if sc > bestScore {
bestScore = sc
copy(bestPerm, p2)
}
}
}
out := bufio.NewWriter(os.Stdout)
for i := 0; i < 12; i++ {
fmt.Fprintf(out, "%c", 'a'+bestPerm[i])
}
fmt.Fprintln(out)
out.Flush()
}
func abs(a int) int {
if a < 0 {
return -a
}
return a
}