123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- package kata
- import (
- "fmt"
- "sort"
- "strings"
- "unicode"
- )
- type count map[rune]int
- func (c count) String() (s string) {
- var sl []string
- for r := 'a'; r <= 'z'; r++ {
- n, ok := c[r]
- if ok {
- sl = append(sl, fmt.Sprintf("%c: %d", r, n))
- }
- }
- s = "[" + strings.Join(sl, ", ") + "]"
- return s
- }
- func (c count) ascending() []string {
- res := make([]string, len(c))
- i := 0
- for r, n := range c {
- res[i] = string(rune(n)) + string(r)
- i++
- }
- sort.Strings(res)
- return res
- }
- func newCount() count {
- c := make(count, 26)
- for r := 'a'; r <= 'z'; r++ {
- c[r] = 0
- }
- return c
- }
- func (c count) ingest(s string) {
- // Count letters.
- for _, r := range s {
- if unicode.IsLower(r) {
- c[r]++
- }
- }
- // Remove non-repeated letters and order.
- for r := 'a'; r <= 'z'; r++ {
- occurrences := c[r]
- if occurrences <= 1 {
- delete(c, r)
- continue
- }
- }
- }
- type hitList map[int]count
- func (h hitList) descendingKeys() []int {
- keys:= make([]int, len(h))
- i := 0
- for k := range h {
- keys[i] = k
- i++
- }
- sort.Sort(sort.Reverse(sort.IntSlice(keys)))
- return keys
- }
- func (h hitList) String() string {
- var sl []string
- for n, c := range h {
- sl = append(sl, fmt.Sprintf("%d: %s", n, c))
- }
- res := "[" + strings.Join(sl, ", ") + "]"
- return res
- }
- // Cull removes from each count entries not at least equal to the one in the other count.
- func cull(c1, c2 count) (r1, r2 count) {
- r1, r2 = count{}, count{}
- for r, n1 := range c1 {
- n2, ok := c2[r]
- if !ok || n1 >= n2 {
- r1[r] = n1
- }
- }
- for r, n2 := range c2 {
- n1, ok := c1[r]
- if !ok || n2 >= n1 {
- r2[r] = n2
- }
- }
- return
- }
- // Invert build an inverse count, assuming already culled counts.
- func invert(c1, c2 count) hitList {
- union := hitList{}
- for r, n := range c1 {
- if _, ok := union[n]; !ok {
- union[n] = count{}
- }
- // On the first string, every letter is missing, so just initialize.
- union[n][r] = '1'
- }
- for r, n := range c2 {
- if _, ok := union[n]; !ok {
- union[n] = count{}
- }
- // On any subsequent string, letters may exist or not, so be careful.
- _, ok := union[n][r]
- if !ok {
- union[n][r] = '2'
- } else {
- union[n][r] = '='
- }
- }
- return union
- }
- func Mix(s1, s2 string) string {
- c1, c2 := newCount(), newCount()
- c1.ingest(s1)
- c2.ingest(s2)
- c1, c2 = cull(c1, c2)
- union := invert(c1, c2)
- var sl []string
- for _, n := range union.descendingKeys() {
- for _, hits := range union[n].ascending() {
- source, r := hits[0], hits[1]
- sl = append(sl, fmt.Sprintf("%c:%s", source, strings.Repeat(string(r), n)))
- }
- }
- s := strings.Join(sl, "/")
- return s
- }
|