Lateinisches Worturteil

8

Da ich mich nicht länger als 5 Sekunden auf eine Aufgabe konzentrieren kann, teile ich Wörter häufig in Teilzeichenfolgen auf, die jeweils eine andere Länge haben und keine wiederholten Zeichen enthalten. Zum Beispiel könnte das Wort "Pasta" in "Past" & "A", "Pas" & "Ta" oder "Pa" & "Sta" aufgeteilt werden und Sie erhalten das Bild.

Da es jedoch schwierig ist, sich an alle Kombinationen zu erinnern, wähle ich im Allgemeinen nur eine aus, und ich wähle gerne die schönste aus. Wir betrachten den schönsten Weg als den mit der niedrigsten "Punktzahl". Ihre Aufgabe wird es sein, ein Wort zu geben, um die Punktzahl unter Berücksichtigung der folgenden komplizierten Regeln auszudrucken.

Wertung

Beschreibung, wie man ein Wort bewertet:

  • Ein Wort ist eine Folge von lateinischen Zeichen. Großbuchstaben sollten durch zwei gleiche Kleinbuchstaben ersetzt werden (aus "Box" wird "bbox").

  • Ein Segment ist eine zusammenhängende (strenge) Teilzeichenfolge eines Wortes und darf kein Zeichen zweimal enthalten ("her", "re", "h" sind alle gültigen Segmente von "Here" ("hhere"), aber "hh"). und "ere" sind nicht)

  • Eine Segmentierung ist eine Menge von Segmenten unterschiedlicher Länge, die beim Verbinden das ursprüngliche Wort bilden ("tre" und "e" ergeben "Baum") und die nicht weiter innerhalb der Segmentierung segmentiert werden können (dh "ba" hat ein einzelnes Segmentierung, "ba"; und "alp" & "habet" ist keine gültige Segmentierung von "alphabet", da beide weiter segmentiert werden könnten (z. B. in "a" & "lp" & "habet", was jetzt ist eine gültige Segmentierung ("habet" kann nicht segmentiert werden, ohne ein Segment der Länge 2 oder 1 zu bilden))).

  • Die Punktzahl einer Segmentierung ist die Summe der Punktzahlen jedes einzelnen Zeichens, das im ursprünglichen Wort vorkommt (sobald Großbuchstaben ersetzt wurden).

  • Die Bewertung der Zeichen wird unten erläutert

  • Die Punktzahl eines Wortes ist die Punktzahl seiner bestmöglichen Segmentierung (die mit der niedrigsten Punktzahl).

Wenn für ein Wort keine gültigen Segmentierungen vorhanden sind (z. B. "Brass" ("bbrass"), die nicht segmentiert werden können, da sich das erste "b" und das letzte "s" in ihren eigenen Segmenten befinden müssten, würde dies resultieren in zwei Segmenten gleicher Länge), dann sollten Sie den Text "böse" ausgeben, andernfalls sollten Sie die Punktzahl des Wortes ausgeben.

Charakterwertung

Die Bewertung von Zeichen basiert auf der Häufigkeit, mit der das Zeichen angezeigt wird, und der Gewichtung der Segmente, in denen es angezeigt wird. Die Gewichtung der Segmente hängt von der Länge des Segments und dem niedrigsten gemeinsamen Vielfachen der Länge aller Segmente in ab die Segmentierung.

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

Betrachten Sie das Wort "Olive", das als "ol" & "ive" segmentiert und als 2 Kästchen desselben Bereichs dargestellt werden kann, eines von "ol" mit Gewicht 3 und eines von "ive" mit Gewicht 2 (LCM) von 6).

ol
ol ive
ol ive

Dies soll die zwei Kästchen darstellen, eine aus 3 "ol" und eine aus 2 "ive". Alternativ könnte es "o" & "live" sein (LCM von 4)

o
o
o
o live

Die Punktzahl jedes Zeichens ist dann die Summe der Gewichte der Segmente, in denen es erscheint, multipliziert mit der Häufigkeit, mit der es nach dem Ersetzen von Großbuchstaben erscheint. Wenn es also zweimal erscheint, wird Ihnen für jedes Mal, wenn Sie es sagen müssen, das Doppelte berechnet ).

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

Bewertungsbeispiel

Wir nehmen das Wort "fallen", es kann nur in "fal" und "l" unterteilt werden. Das niedrigste gemeinsame Vielfache von 3 und 1 ist 3, also hat "fal" das Gewicht 1 und "l" das Gewicht 3.

    l
    l
fal l

Durch jeden Charakter gehen ...

  • "f" erscheint einmal und befindet sich im Segment "fal" mit Gewicht 1, hat also Punktzahl 1 * 1 = 1

  • "a" erscheint auch nur einmal, hat die Summe der Gewichte von 1, hat also die Punktzahl 1 * 1 = 1

  • "l" erscheint zweimal und erscheint in "fal" (Gewicht 1) und "l" (Gewicht 3), hat also Punktzahl 2 * (1 + 3) = 8

Die Summe davon ist 10 (die Punktzahl der Segmentierung und des Wortes, da dies die schönste Segmentierung ist). Hier ist dies im gleichen Format wie in den folgenden Beispielen:

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

Beispiel Scorings

Diese Beispiele für Wertungen können helfen oder auch nicht:

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

"Buch" ist ein böses Wort, hat also keine Punktzahl.

Beachten Sie, dass "Schatz" auf verschiedene Arten segmentiert werden kann. Die gezeigte Segmentierung profitiert jedoch davon, dass die längeren Buchstaben ("r" und "e") in den längeren Segmenten enthalten sind, sodass sie nicht so viel Gewicht haben. Die Segmentierung "t" & "re" & "asure" würde das gleiche Ergebnis liefern, während "Treas" & "ur" & "e" leiden würde, wobei "e" eine Punktzahl von 2 * (1 + 10 + 2) hat ) = 24 alleine. Diese Beobachtung ist wirklich der Geist der gesamten Übung. Ein Beispiel für eine falsche Bewertung von "Schatz" (falsch, weil sie nicht aus der Bewertung der schönsten Segmentierung (der mit der niedrigsten Bewertung) abgeleitet ist):

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

Eingang

Eine einzelne Zeichenfolge, die in beiden Fällen nur lateinische Zeichen enthält ("Pferd", "Pferd" und "hOrSe" sind alle gültigen Eingaben), kann entweder von STDIN, Befehlszeilenargument, Funktionsargument oder auf andere Weise akzeptiert werden, wenn Ihre Sprache von Auswahl unterstützt keine der oben genannten.

Ausgabe

Sie müssen entweder die Punktzahl des Wortes ausgeben, bei der es sich um eine einzelne positive Ganzzahl größer als 0 handelt, oder "böse", wenn keine Segmentierung vorhanden ist. Die Ausgabe sollte an STDOUT oder das Rückgabeargument einer Funktion erfolgen, es sei denn, die Sprache Ihrer Wahl unterstützt keine dieser Funktionen. In diesem Fall sollten Sie etwas Sportliches tun.

Beispiele

Ich erwarte nicht, dass Sie all dieses Zeug drucken, alles was ich will ist die Punktzahl des Wortes oder die Ausgabe "böse" zum Beispiel (Eingabe gefolgt von Ausgabe)

eye
7

Eel
evil

a
1

Establishments
595

antidisestablishmentarianism
8557

Ich mache mir keine Sorgen um die Leistung. Wenn Sie auf einer vernünftigen (absichtlich vagen) Maschine in weniger als einer Minute fast jedes 15-Buchstaben-Wort (nach dem Ersetzen von Großbuchstaben) erzielen können, ist das gut genug für mich.

Dies ist Code-Golf, kann der kürzeste Code gewinnen.

Vielen Dank an Peter Taylor, Martin Büttner und SP3000 für ihre Hilfe bei dieser Herausforderung

VisualMelon
quelle
4
Wenn Sie sich nicht länger als 5 Sekunden auf eine Aufgabe konzentrieren können, muss das Schreiben dieser Herausforderung für immer gedauert haben!
Alex A.

Antworten:

5

Mathematica, 373 Bytes

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]&

Das ist ziemlich lang ... und auch ziemlich naiv. Es definiert eine unbenannte Funktion, die die Zeichenfolge verwendet und die Punktzahl zurückgibt. Die Bearbeitung dauert ungefähr 1 Sekunde "Establishments", liegt also innerhalb des Zeitlimits. Ich habe eine etwas kürzere Version, die verwendet Combinatorica`SetPartitions, aber es dauert bereits eine Minute für "Establishme".

Hier ist eine Version mit Leerzeichen:

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
] &

Ich könnte später eine detailliertere Erklärung hinzufügen. Dieser Code verwendet die zweite Lösung aus dieser Antwort , um alle Partitionen abzurufen, und diese Lösung , um sicherzustellen, dass sie alle maximal segmentiert sind.

Martin Ender
quelle
2

Java 8, 1510 1485 Bytes

Das ist viel zu lang. Kombinatorik ist in Java nie einfach. Es kann definitiv einiges gekürzt werden. Rufen Sie an mit a(string). Dies verwendet einen Exponentialspeicher- und Zeitalgorithmus; Erwarten Sie also nicht, dass es für lange Eingaben funktioniert. Die Verarbeitung dauert etwa eine halbe Sekunde Establishments. Es stürzt mit einem Speicherfehler für ab 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;}

Versuch es hier

Mit Erklärung eingerückt:

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;
}

Dies missbraucht auch Generika ziemlich stark, um die Anzahl der Bytes zu reduzieren. Ich bin ziemlich überrascht, dass ich alles zum Kompilieren bringen konnte.

Danke Ypnypn :)

Die Nummer eins
quelle
Wow, das ist beeindruckend! Ein paar Golfnotizen: Es gibt zusätzliche Leerzeichen in der i.put...Linie und der while-Schleife; Ich denke while(d!=0) kann sein while(d>0); es gibt keine Notwendigkeit für new ArrayListam Ende, da Arrays.asListes ArrayListsowieso gibt ; In der letzten Methode können Sie bals Ebene definieren List.
Ypnypn
@Ypnypn Vielen Dank für Ihre Vorschläge :) Arrays.asListgibt eine nicht veränderbare zurück ArrayList, daher kann ich das nicht verwenden, ohne eine zu bekommen OperationNotSupportedException. bkann eine einfache Liste sein, muss aber ceine bleiben List<List<String>>. Ich werde nachsehen, ob es while(d>0)morgen funktioniert.
TheNumberOne
2

C # 679 Bytes

Diese Lösung basiert grob auf der Struktur meiner ursprünglichen Testimplementierung und war anfangs nur ein Golf-Re-Write, aber dann habe ich alle Funktionen eingebunden, und jetzt ist es schrecklich. Es ist ziemlich schnell und löst Establishments in weniger als einer Sekunde. Es ist ein vollständiges Programm, das das Eingabewort als einen einzigen Parameter von ARGV verwendet.

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;}}

Die MainMethode erstellt zunächst eine Kopie der Eingabe, wobei die Großbuchstaben ersetzt werden. Es ruft dann Sden "Löser" auf, der die Punktzahl einer gegebenen Segmentierung zurückgibt (die erste Segmentierung ist die mit einem einzelnen Segment, das das ganze Wort ist). Je nach Punktzahl wird dann entweder "böse" oder die Partitur gedruckt.

Der "Solver" ( S) erledigt alle interessanten Dinge und wurde ursprünglich in 4 Methoden unterteilt, die zusammengerollt wurden. Es funktioniert, indem zuerst die aktuelle Segmentierung bewertet wird und notiert wird, ob sie ungültig ist (und vor allem, ob sie so ungültig ist, dass wir nicht versuchen sollten, sie weiter zu segmentieren (für die Leistung), und den Rest der Berechnung überspringen, wenn dies der Fall ist). . Wenn es dann nicht übersprungen wurde, teilt es jedes Segment in der Segmentierung überall dort auf, wo es geteilt werden kann, und findet die beste Punktzahl von all diesen (rekursiv aufrufend S). Schließlich wird entweder die beste Punktzahl der untergeordneten Segmentierungen zurückgegeben, andernfalls die eigene Punktzahl, oder sie ist ungültig, wenn die eigene Segmentierung ungültig ist.

Code mit Kommentaren:

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
quelle