Penghakiman Kata Latin

8

Karena saya tidak dapat berkonsentrasi pada tugas apa pun selama lebih dari 5 detik, saya sering menemukan diri saya memecah kata menjadi sub-string, yang masing-masing memiliki panjang yang berbeda dan tidak mengandung karakter yang berulang. Misalnya, kata "pasta" mungkin dipecah menjadi "masa lalu" & "a", "pas" & "ta", atau "pa" & "sta" dan Anda mendapatkan gambarnya.

Namun, karena mengingat semua kombinasi itu sulit, saya biasanya hanya memilih satu, dan saya suka memilih yang terbaik. Kami menganggap cara terbaik untuk menjadi yang memiliki "skor" terendah. Tugas Anda akan, diberi kata, untuk mencetak skornya, diberi aturan rumit berikut.

Mencetak gol

Deskripsi cara mencetak kata:

  • Sebuah kata adalah rangkaian karakter Latin, huruf besar harus diganti dengan 2 dari huruf kecil yang sama (jadi "Kotak" menjadi "bbox")

  • Segmen adalah substring berdekatan (ketat) dari sebuah kata, dan tidak boleh mengandung karakter dua kali ("dia", "re", "h" semua segmen yang valid dari "Di sini" ("di sini"), tetapi "hh" dan "ere" tidak)

  • Segmentasi adalah seperangkat segmen dengan panjang berbeda yang, ketika digabungkan, membentuk kata asli ("tre" dan "e" membuat "pohon"), dan yang tidak dapat lagi disegmentasi dalam segmentasi (yaitu "ba" memiliki satu segmentasi, "ba"; dan "alp" & "habet" bukan segmentasi yang valid dari "alfabet", karena salah satu dari ini dapat disegmentasi lebih lanjut (misalnya menjadi "a" & "lp" & "habet", yang sekarang segmentasi yang valid ("habet" tidak dapat disegmentasi tanpa membentuk segmen dengan panjang 2 atau 1)))).

  • Skor segmentasi adalah jumlah skor masing-masing karakter berbeda yang muncul dalam kata asli (setelah huruf kapital diganti)

  • Skor karakter dijelaskan di bawah ini

  • Skor sebuah kata adalah skor dari segmentasi terbaik yang mungkin (dengan skor terendah)

Jika tidak ada segmentasi yang valid untuk sebuah kata (misalnya, "Kuningan" ("bbrass"), yang tidak dapat disegmentasi karena "b" dan "s" terakhir harus di segmen mereka sendiri, yang akan menghasilkan dalam dua segmen dengan panjang yang sama), maka Anda harus menampilkan teks "jahat", jika tidak, Anda harus menampilkan skor kata.

Penilaian karakter

Skor karakter didasarkan pada berapa kali karakter muncul, dan bobot segmen yang muncul masuk. Bobot segmen tergantung pada panjang segmen, dan kelipatan umum terendah dari panjang semua segmen di segmentasi.

segment weighting = lowest common multiple of lengths segments / length of segment

Pertimbangkan kata "zaitun", yang dapat disegmentasi sebagai "ol" & "ive", dan divisualisasikan sebagai 2 kotak dari area yang sama, satu "ol" dengan berat 3, dan satu "ive" dengan berat 2 (LCM dari 6).

ol
ol ive
ol ive

Ini dimaksudkan untuk menggambarkan dua kotak, satu terbuat dari 3 "ol", dan satu terbuat dari 2 "ive". Atau, mungkin "o" & "hidup" (LCM dari 4)

o
o
o
o live

Skor masing-masing karakter adalah jumlah dari bobot segmen di mana ia muncul, dikalikan dengan jumlah kali itu muncul setelah mengganti huruf besar (jadi jika itu muncul dua kali, Anda akan dikenakan biaya dua kali lipat untuk setiap kali Anda harus mengatakannya ).

character score = character count * sum(segment weights in which character appears)

Contoh pemberian skor

Kami mengambil kata "jatuh", itu hanya dapat dibagi menjadi "fal" dan "l". Kelipatan umum terendah 3 dan 1 adalah 3, jadi "fal" memiliki bobot 1, dan "l" memiliki berat 3.

    l
    l
fal l

Melewati setiap karakter ...

  • "f" muncul satu kali, dan berada di segmen "fal" dengan bobot 1, sehingga memiliki skor 1 * 1 = 1

  • "a" juga muncul hanya sekali, memiliki jumlah bobot 1, sehingga memiliki skor 1 * 1 = 1

  • "l" muncul dua kali, dan muncul di "fal" (bobot 1) dan "l" (bobot 3), sehingga memiliki skor 2 * (1 + 3) = 8

Jumlahnya adalah 10 (skor segmentasi, dan kata, karena ini adalah segmentasi terbaik). Berikut ini dalam format yang sama dengan contoh di bawah ini:

fall = fal l
2*1 [fa] + 2*(1+3) [ll] = 10

Contoh Penilaian

Contoh-contoh penilaian ini mungkin atau mungkin tidak membantu:

class -> clas s
3*1 [cla] + 2*(4+1) [ss] = 13

fish -> fis h
3*1 [fis] + 1*3 [h] = 6

eye -> e ye
1*1 [y] + 2*(1+2) [ee] = 7

treasure -> treas u re
3*2 [tas] + 2*2*(2+5) [rree] + 1*10 [u] = 44

Wolf -> w wolf
3*1 [olf] + 2*(1+4) = 13

book
evil

"buku" adalah kata yang jahat, jadi tidak ada nilainya.

Perhatikan bahwa "harta" dapat tersegmentasi dalam beberapa cara, tetapi segmentasi menunjukkan manfaat dari memiliki huruf yang lebih sering ("r" dan "e") di segmen yang lebih panjang, sehingga mereka tidak memiliki bobot lebih banyak. Segmentasi "t" & "re" & "asure" akan memberikan hasil yang sama, sedangkan "harta" & "ur" & "e" akan menderita, dengan "e" memiliki skor 2 * (1 + 10 + 2 ) = 24 dengan sendirinya. Pengamatan ini benar-benar semangat dari seluruh latihan. Contoh skor "harta" yang salah (salah karena tidak berasal dari skor segmentasi terbaik (yang dengan skor terendah)):

treasure = treas ur e
3*2 [tas] + 2*(2+5) [rr] + 1*5 [u] + 2*[2+10] = 49

Memasukkan

Satu string yang hanya berisi karakter latin dari kedua case ("horse", "Horse", dan "hOrSe" adalah semua input yang valid) yang dapat diterima baik oleh STDIN, argumen baris perintah, argumen fungsi, atau sebaliknya jika bahasa Anda dari Pilihan tidak mendukung salah satu dari yang disebutkan di atas.

Keluaran

Anda harus menampilkan skor kata, yang merupakan bilangan bulat positif tunggal lebih besar dari 0, atau "jahat" jika tidak ada segmentasi. Outputnya harus STDOUT atau argumen pengembalian fungsi, kecuali bahasa pilihan Anda tidak mendukung keduanya, dalam hal ini melakukan sesuatu dengan sportif.

Contohnya

Saya tidak mengharapkan Anda untuk mencetak semua hal ini, yang saya inginkan hanyalah skor kata, atau output "jahat", misalnya (input diikuti oleh output)

eye
7

Eel
evil

a
1

Establishments
595

antidisestablishmentarianism
8557

Saya tidak peduli dengan kinerja, jika Anda dapat mencetak hampir setiap kata 15letter (setelah mengganti huruf kapital) dalam waktu kurang dari satu menit pada mesin yang masuk akal (sengaja dibiarkan kabur), itu cukup baik bagi saya.

Ini kode-golf, semoga kode terpendek menang.

Terima kasih kepada PeterTaylor, MartinBüttner, dan SP3000 untuk bantuan mereka dengan tantangan ini

VisualMelon
sumber
4
Jika Anda tidak dapat berkonsentrasi pada tugas apa pun selama lebih dari 5 detik, menuliskan tantangan ini pasti akan membawa Anda selamanya!
Alex A.

Jawaban:

5

Mathematica, 373 byte

If[l=Length;a=Accumulate[l/@#]&;u=Unequal;e=Select;{}==(m=(g=#;Tr[#2Flatten[ConstantArray[#,LCM@@l/@g/l@#]&/@g]~Count~#&@@@Tally@c])&/@e[p=e[c~Internal`PartitionRagged~#&/@Join@@Permutations/@IntegerPartitions[l[c=Characters[s=StringReplace[#,c:"A"~CharacterRange~"Z":>(d=ToLowerCase@c)<>d]]]],u@@l/@#&&And@@u@@@#&],FreeQ[p,l_List/;#!=l&&SubsetQ[a@l,a@#]]&]),"evil",Min@m]&

Ini cukup lama ... dan juga agak naif. Ini mendefinisikan fungsi tanpa nama yang mengambil string dan mengembalikan skor. Dibutuhkan sekitar 1 detik untuk menangani "Establishments", jadi itu masih dalam batas waktu. Saya punya versi yang sedikit lebih pendek yang digunakan Combinatorica`SetPartitions, tetapi sudah butuh satu menit "Establishme".

Ini adalah versi dengan spasi putih:

If[
  l = Length;
  a = Accumulate[l /@ #] &;
  u = Unequal;
  e = Select;
  {} == (
    m = (
      g = #;
      Tr[
        #2 Flatten[
          ConstantArray[
            #, 
            LCM @@ l /@ g/l@#
          ] & /@ g
        ]~Count~# & @@@ Tally@c
      ]
    ) & /@ e[
      p = e[
        c~Internal`PartitionRagged~# & /@ Join @@ Permutations /@ IntegerPartitions[
          l[
            c = Characters[
              s = StringReplace[
                #, 
                c : "A"~CharacterRange~"Z" :> (d = ToLowerCase@c) <> d
              ]
            ]
          ]
        ], 
        u @@ l /@ # && And @@ u @@@ # &
      ], 
      FreeQ[p, l_List /; # != l && SubsetQ[a@l, a@#]] &
    ]
  ),
  "evil",
  Min@m
] &

Saya mungkin menambahkan penjelasan yang lebih rinci nanti. Kode ini menggunakan solusi kedua dari jawaban ini untuk mendapatkan semua partisi dan solusi ini untuk memastikan mereka semua tersegmentasi secara maksimal.

Martin Ender
sumber
2

Java 8, 1510 1485 byte

Ini adalah cara yang terlalu panjang. Combinatorics tidak pernah mudah di java. Pasti bisa disingkat sedikit. Telepon dengana(string) . Ini menggunakan memori eksponensial dan algoritma waktu; jadi jangan berharap itu bekerja untuk input lama. Dibutuhkan sekitar setengah detik untuk diproses Establishments. Itu crash dengan kehabisan memori kesalahan untuk antidisestablishmentarianism.

import java.util.*;import java.util.stream.*;void a(String a){a=a.replaceAll("\\p{Upper}","$0$0").toLowerCase();List<List<String>>b=b(a),c;b.removeIf(d->d.stream().anyMatch(e->e.matches(".*(.).*\\1.*")));b.removeIf(d->{for(String e:d)for(String f:d)if(e!=f&e.length()==f.length())return 1>0;return 1<0;});c=new ArrayList(b);for(List<String>d:b)for(List<String>e:b){if(d==e)continue;int f=0,g=0;List h=new ArrayList(),i=new ArrayList();for(String j:d)h.add(f+=j.length());for(String k:e)i.add(g+=k.length());if(i.containsAll(h))c.remove(d);else if(h.containsAll(i))c.remove(e);}b=c;int d=-1;for(List e:b)d=d<0?c(e):Math.min(c(e),d);System.out.println(d<0?"evil":d);}int c(List<String>a){List<Integer>b=a.stream().map(c->c.length()).collect(Collectors.toList()),d;int e=d(b.toArray(new Integer[0])),f=0,g=0,h;d=b.stream().map(A->e/A).collect(Collectors.toList());Map<Character,Integer>i=new HashMap(),j=new HashMap();for(;g<a.size();g++){h=d.get(g);String k=a.get(g);for(char l:k.toCharArray()){i.put(l,i.getOrDefault(l,0)+1);j.put(l,j.getOrDefault(l,0)+h);}}for(char k:i.keySet())f+=i.get(k)*j.get(k);return f;}int d(Integer...a){int b=a.length,c,d,e;if(b<2)return a[0];if(b>2)return d(a[b-1],d(Arrays.copyOf(a,b-1)));c=a[0];d=a[1];for(;d>0;d=c%d,c=e)e=d;return a[0]*a[1]/c;}List b(String a){List<List>b=new ArrayList(),c;for(int i=0;++i<a.length();b.addAll(c)){String d=a.substring(0,i),e=a.substring(i);c=b(e);for(List f:c)f.add(0,d);}b.add(new ArrayList(Arrays.asList(a)));return b;}

Coba di sini

Diindentasi dengan penjelasan:

void a(String a){
    a = a.replaceAll("\\p{Upper}", "$0$0").toLowerCase();                //Replace all uppercase letters with two lowercase letters

    List<List<String>> b = b(a), c;                                      //Generate partitions
    b.removeIf(d -> d.stream().anyMatch(e -> e.matches(".*(.).*\\1.*")));//Remove partitions that contains duplicate letters

    b.removeIf(d -> {                                                    //Remove partitions that have equal length parts
        for (String e : d)
            for (String f : d)
                if (e != f & e.length() == f.length())
                    return 1 > 0;
        return 1 < 0;
    });

    c = new ArrayList(b);                                                //Remove partitions that can be partitioned further
    for (List<String> d : b)                                             //Uses Martin's technique
        for (List<String> e : b){
            if (d == e)
                continue;
            int f = 0, g = 0;
            List h = new ArrayList(), i = new ArrayList();
            for (String j : d)
                h.add(f += j.length());
            for (String k : e)
                i.add(g += k.length());
            if (i.containsAll(h))
                c.remove(d);
            else if (h.containsAll(i))
                c.remove(e);
        }

    b = c;

    int d = -1;
    for (List e : b)
        d = d < 0 ? c(e) : Math.min(c(e), d);                            //Find nicest partition

    System.out.println(d < 0 ? "evil" : d);
}

int c(List<String> a) {                                                  //Grade a partition
    List<Integer> b = a.stream().map(c->c.length()).collect(Collectors.toList()), d; //Map to length of parts

    int e = d(b.toArray(new Integer[0])), f = 0, g = 0, h;

    d = b.stream().map(A -> e / A).collect(Collectors.toList());         //Figure out the weight of each part

    Map<Character, Integer> i = new HashMap(), j = new HashMap();

    for (; g < a.size(); g++){                                           //Count instances of each character and
        h = d.get(g);                                                    //weight of each character
        String k = a.get(g);
        for (char l : k.toCharArray()){
            i.put(l, i.getOrDefault(l, 0) + 1);
            j.put(l, j.getOrDefault(l, 0) + h);
        }
    }

    for (char k : i.keySet())
        f += i.get(k) * j.get(k);                                        //Sum cost of each character

    return f;
}

int d(Integer... a) {                                                    //Find lcm
    int b = a.length, c, d, e;
    if (b < 2)
        return a[0];
    if (b > 2)
        return d(a[b - 1], d(Arrays.copyOf(a, b - 1)));
    c = a[0];
    d = a[1];
    for (;d > 0;d = c % d, c = e)
        e = d;
    return a[0] * a[1] / c;
}

List b(String a) {                                                       //Find all partitions of a string
    List<List> b = new ArrayList(), c;                                   //Uses recursion
    for (int i = 0; ++i < a.length();b.addAll(c)){
        String d = a.substring(0, i), e = a.substring(i);
        c = b(e);
        for (List f : c)
            f.add(0, d);
    }
    b.add(new ArrayList(Arrays.asList(a)));
    return b;
}

Ini juga sedikit menyalahgunakan obat generik untuk mengurangi jumlah byte. Saya cukup terkejut saya bisa mendapatkan semua itu untuk dikompilasi.

Terima kasih Ypnypn :)

TheNumberOne
sumber
Wow, ini mengesankan! Beberapa catatan bermain golf: Ada spasi tambahan di dalam i.put...garis dan loop sementara; Saya pikir while(d!=0) bisa while(d>0); tidak perlu new ArrayListpada akhirnya karena Arrays.asListmemberi ArrayListpula; dalam metode terakhir, Anda dapat mendefinisikan bsebagai dataran List.
Ypnypn
@ Ypnypn Terima kasih atas saran Anda :) Arrays.asListmengembalikan yang tidak dapat dimodifikasi ArrayList, jadi saya tidak dapat menggunakannya tanpa mendapatkan OperationNotSupportedException. bdapat berupa daftar sederhana, tetapi cperlu tetap a List<List<String>>. Saya akan memeriksa untuk melihat apakah while(d>0)bekerja besok.
TheNumberOne
2

C # 679 Bytes

Solusi ini kira-kira didasarkan pada struktur implementasi pengujian awal saya, dan awalnya hanya menulis ulang golf, tapi kemudian saya sebaris semua fungsi, dan sekarang ini mengerikan. Ini cukup cepat, menyelesaikan perusahaan dalam waktu kurang dari satu detik. Ini adalah program lengkap yang menggunakan kata input sebagai parameter tunggal ARGV.

using Q=System.String;class P{static void Main(Q[]A){Q s="";foreach(var c in A[0]){var z=(char)(c|32);if(c<z)s+=z;A[0]=s+=z;}int r=S(A);System.Console.WriteLine(r<1?"evil":""+r);}static int S(Q[]s){int r=0,t,j,k,L=1,Z=s.Length,i=Z,T=0,R=2;for(;i-->0;R=t<1?0:R){t=s[i].Length;k=L;for(j=2;t>1;)if(t%j++<1){t/=--j;if(k%j<1)k/=j;else L*=j;}}foreach(var C in Q.Join("",s))for(i=Z;i-->0;){for(k=s[j=i].Length;j-->0;)R=k==s[j].Length?0:R;j=s[i].IndexOf(C)+1;R=j*R*s[i].IndexOf(C,j)>1?1:R;T+=j>0?L/k:0;}i=R<1?0:Z;for(var U=new Q[Z+1];i-->0;)for(j=s[i].Length;j-->1;r=r<1|((t=S(U))>0&t<r)?t:r)for(U[k=Z]=s[i].Substring(j);k-->0;U[i]=s[i].Substring(0,j))U[k]=s[k];return r<1?R<2?R-1:T:r;}}

The Mainmetode dimulai dengan membuat salinan input dengan ibukota diganti. Ia kemudian memanggil S, "solver", yang mengembalikan skor segmentasi tertentu (segmentasi pertama adalah bahwa dengan segmen tunggal yang merupakan keseluruhan kata). Ini kemudian mencetak "jahat" atau skor, tergantung pada skor.

"Solver" ( S) melakukan semua hal yang menarik, dan pada awalnya dipecah menjadi 4 metode, yang digulung bersama. Ia bekerja dengan terlebih dahulu menilai segmentasi saat ini, membuat catatan apakah itu tidak valid (dan yang terpenting, apakah itu sangat tidak valid, kita tidak boleh mencoba untuk lebih jauh lagi segmentasi itu (untuk kinerja), melewatkan sisa perhitungan jika itu) . Kemudian, jika belum dilewati, ia membagi setiap segmen dalam segmentasi di mana saja ia dapat dipecah, dan menemukan skor terbaik dari semua ini (panggilan secara rekursif S). Akhirnya, itu baik mengembalikan skor terbaik dari Segmen bawahan, selain itu skor sendiri, atau tidak valid jika segmentasi sendiri tidak valid.

Kode dengan komentar:

using Q=System.String; // this saves no bytes now

class P
{
    // boring
    static void Main(Q[]A)
    {
        // this can surely be shorter
        // replace capitals
        // I refuse to put this in S (would give us a Q, but we'd have to pay for the Action=null)
        Q s="";

        foreach(var c in A[0])
        {
            var z=(char)(c|32); // ugly
            if(c<z)
                s+=z; // ugly
            A[0]=s+=z; // reuse the array
        }

        int r=S(A); // reuse the array
        System.Console.WriteLine(r<1?"evil":""+r);
    }

    // solve
    static int S(Q[]s) // s is current solution
    {        
        int r=0,t,j,k,
        L=1,Z=s.Length,i=Z,
        T=0, // is score for this solution (segmentation)
        R=2; // R is current return value, as such, 0 -> return -1, 1 -> return 0, 2 -> return T

        // score first
        // factorise stuff, L is LCM
        for(;
            i-->0; // for each segment
            R=t<1?0:R) // segment too short (skip)
        {
            t=s[i].Length;

            k=L; // we cut up k as we cut up c, when we can't cut up k, we need to build up L
            for(j=2;t>1;)
                if(t%j++<1) // j goes into t
                {
                    t/=--j; // cut up t
                    if(k%j<1) // j goes into k
                        k/=j; // cut up k
                    else
                        L*=j; // j does not go into k, build up L
                }
        }

        // recycle i, j, k, (t)

        foreach(var C in Q.Join("",s)) // for each character
            for(i=Z;i-->0;) // for each segment
            {
                for(k=s[j=i].Length;
                    j-->0;) // for all the segments that follow
                    R=k==s[j].Length?0:R; // repeat length (skip)

                j=s[i].IndexOf(C)+1;

                // these both check if this segment contains the char (j > 0)
                R=j*R*s[i].IndexOf(C,j)>1?1:R; // duplicate char (allow)

                T+=j>0?L/k:0; // add on the segment weight
            }

        // R is whether we are invalid or not
        // T is our score

        i=R<1?0:Z; // if segmentation invalid and can't be segmented, skip everything (performance)

        // recycle i, j, k, t
        // first use of r=0

        for(var U=new Q[Z+1];
            i-->0;) // for each segment
            for(j=s[i].Length;
                j-->1; // for each place we can split it
                r=r<1|((t=S(U))>0&t<r)?t:r) // solve where we split thing at i at position j, if this score is better than r, replace r with it
                for(U[k=Z]=s[i].Substring(j); // put second half of s[i] in last position (order doesn't matter)
                    k-->0; // for each char in s[i]
                    U[i]=s[i].Substring(0,j)) // put first half of s[i] in p position
                    U[k]=s[k]; // copy across everything else

        return r<1?R<2?R-1:T:r; // if we didn't find a valid solution more segmented than this, return our score (unless we are invalid, in which case, return R-1), else the score for the more segmentation solution
    }
}
VisualMelon
sumber