Lokale Saitenperioden

20

Lokale Zeiträume

Nehmen Sie eine nicht leere Zeichenfolge s . Die lokale Periode von s am Index i ist die kleinste positive ganze Zahl n, so dass wir für jede 0 ≤ k <n s [i + k] = s [i-n + k] haben, wenn beide Seiten definiert sind. Alternativ ist es die minimale Länge einer nicht leeren Zeichenfolge w, so dass, wenn die Verkettung ww neben s platziert wird, so dass die zweite Kopie von w am Index i von s beginnt , die beiden Zeichenfolgen übereinstimmen, wo immer sie sich überlappen.

Als Beispiel berechnen wir die lokale Periode von s = "abaabbab" bei (0-basiertem) Index 2.

  • Versuchen Sie n = 1 : dann ist s [2 + 0] ≠ s [2-1 + 0] , daher ist diese Auswahl nicht korrekt.
  • Versuchen n = 2 : dann ist s [2 + 0] = s [2-2 + 0], aber s [2 + 1] ≠ s [2-2 + 1] , also ist dies auch nicht korrekt.
  • Versuchen Sie es mit n = 3 : dann s [2 + 0-3] nicht definiert, s [2 + 1] = s [2-3 + 1] und s [2 + 2] = s [2-3 + 2] . Somit ist die Ortszeit 3.

Hier ist eine Visualisierung der lokalen Perioden mit der zweiten Definition, wobei der Übersichtlichkeit halber Semikolons zwischen den beiden Kopien von w eingefügt sind :

index      a b a a b b a b      period
 0       a;a                     1
 1       b a;b a                 2
 2       a a b;a a b             3
 3             a;a               1
 4     b b a b a a;b b a b a a   6
 5                 b;b           1
 6               a b b;a b b     3
 7                   b a;b a     2

Beachten Sie, dass w nicht unbedingt eine Unterzeichenfolge von s ist . Dies geschieht hier im Fall von Index-4.

Die Aufgabe

Ihre Eingabe ist eine nicht leere Zeichenkette s von Klein ASCII - Zeichen. Falls gewünscht, kann es als Liste von Zeichen verwendet werden. Ihre Ausgabe soll die Liste sein, die die lokale Periode von s an jedem ihrer Indizes enthält. Im obigen Beispiel wäre die korrekte Ausgabe [1,2,3,1,6,1,3,2] .

Die niedrigste Byteanzahl in jeder Sprache gewinnt. Es gelten die Standardregeln für .

Testfälle

a -> [1]
hi -> [1, 2]
www -> [1, 1, 1]
xcxccxc -> [1, 2, 2, 5, 1, 3, 2]
abcbacb -> [1, 4, 7, 7, 7, 3, 3]
nininini -> [1, 2, 2, 2, 2, 2, 2, 2]
abaabbab -> [1, 2, 3, 1, 6, 1, 3, 2]
woppwoppw -> [1, 4, 4, 1, 4, 4, 4, 1, 4]
qwertyuiop -> [1, 10, 10, 10, 10, 10, 10, 10, 10, 10]
deededeededede -> [1, 3, 1, 5, 2, 2, 5, 1, 12, 2, 2, 2, 2, 2]
abababcabababcababcabababcaba -> [1, 2, 2, 2, 2, 7, 7, 7, 7, 2, 2, 2, 19, 19, 5, 5, 2, 5, 5, 12, 12, 2, 2, 2, 7, 7, 5, 5, 2]
Zgarb
quelle
@Arnauld Du findest immer ein w mit der gleichen Länge wie s . Im Fall von qwertyuiopist w eine gedrehte Version von qwertyuiop. Siehe auch das Beispiel bei Index 4: w ist nicht unbedingt ein Teilstring von s .
Zgarb
Das macht Sinn. Ich habe die Herausforderung falsch verstanden.
Arnauld
Imaginärer Bonus für eine lineare Zeitlösung! (Jemand anderes kann ein echtes Kopfgeld anbieten, also versuche es weiter.)
user202729
Wirklich nette Herausforderung, aber ich frage mich, ob es sinnvoller wäre, die lokale Periode jeder Position zwischen zwei Zeichen zu definieren (dh wo auch immer das ;in Ihrem Beispiel ist). Das würde die führende 1 loswerden.
Martin Ender
@MartinEnder Das wäre konzeptionell sauberer, aber diese Definition macht es einfacher, die Ausgabe durch Schleifen über die Zeichenfolge zu erstellen, und die Ausgabe wird nicht leer sein.
Zgarb

Antworten:

4

Retina , 89-86 Bytes

.
$`¶$<'¶
/(^|.+)¶.+/_(Lw$`^(.+)?(.*)(.+)?¶(?(1)|(.*))\2(?(3)$)
$2$3$4
G`.
%C`.
N`
0G`

Probieren Sie es online! Bearbeiten: 3 Bytes dank @MartinEnder gespeichert. Erläuterung:

.
$`¶$<'¶

Teilen Sie die Eingabe bei jedem Zeichen auf, und erstellen Sie ein Zeilenpaar, eines für das Präfix und eines für das Suffix des Präfix.

/(^|.+)¶.+/_(

Führen Sie den Rest des Skripts für jedes resultierende Paar aus.

Lw$`^(.+)?(.*)(.+)?¶(?(1)|(.*))\2(?(3)$)
$2$3$4

Finde alle überlappenden Übereinstimmungen und liste die Ergebnisse auf. (Siehe unten.)

G`.

Verwerfen Sie das leere Streichholz.

%C`.

Nimm die Länge jedes Matches.

N`

Numerisch sortieren.

0G`

Nimm den Kleinsten.

Der Abgleich funktioniert, indem Präfix und Suffix in drei Teile geteilt werden. Es sind vier gültige Fälle zu berücksichtigen:

AB|BC   B matches B to the left and B to the right
B|ABC   AB matches [A]B to the left and AB to the right
ABC|B   BC matches BC to the left and B[C] to the right
BC|AB   ABC matches [A]BC to the left and AB[C] to the right

Der reguläre Ausdruck erlaubt daher nur, dass A und C gleichzeitig auf einer Seite übereinstimmen.

Neil
quelle
$&$'ist gleich $<'und die Berechnungsleitungslängen sind kürzer mit %C`.. tio.run/##K0otycxLNPz/X49LJeHQNhUb9UPbuPQ14mr0tDUPbdPT1o/…
Martin Ender
4

Java 8, 167 154 152 Bytes

s->{int l=s.length,r[]=new int[l],i=0,n,k;for(;i<l;r[i++]=n)n:for(n=0;;){for(k=++n;k-->0;)if(i+k<l&i+k>=n&&s[i+k]!=s[i-n+k])continue n;break;}return r;}

-2 Bytes dank @ceilingcat .

Probieren Sie es online aus.

Erläuterung:

s->{                          // Method with char-array parameter and int-array return-type
  int l=s.length,             //  Length of the input-array
      r[]=new int[l],         //  Result-array of the same size 
      i=0,n,k;                //  Integers `i`, `n`, and `k` as defined in the challenge
  for(;i<l;                   //  Loop `i` in the range [0, `l`):
      r[i++]=n)               //    After every iteration: Add `n` to the array
    n:for(n=0;;){             //   Inner loop `n` from 0 upwards indefinitely
      for(k=++n;k-->0;)       //    Inner loop `k` in the range [`n`, 0]:
                              //    (by first increasing `n` by 1 with `++n`)
        if(i+k<l&i+k>=n)      //     If `i+k` and `i-n+k` are both within bounds,
           &&s[i+k]!=s[i-n+k])//     and if `s[i+k]` is not equal to `s[i-n+k]`:
          continue n;         //      Continue loop `n`
                              //    If we haven't encountered the `continue n` in loop `k`:
      break;}                 //     Break loop `n`
  return r;}                  //  Return the result
Kevin Cruijssen
quelle
1

JavaScript (ES6), 84 Byte

Nimmt die Eingabe als Array von Zeichen.

s=>s.map((_,i)=>s.some(_=>s.every(_=>k<j|!s[k]|s[k-j]==s[k++]|k-i>j,++j,k=i),j=0)*j)

Testfälle

Arnauld
quelle
Ich bin mir nicht sicher, ob ein Array von Zeichen zulässig ist. Sind Sie sicher, dass es sich nicht nur um 1-Zeichen-Zeichenfolgen handelt?
Erik der Outgolfer
@EriktheOutgolfer In JS gibt es keinen Zeichentyp, also ja: Technisch handelt es sich um ein Array aus 1-Zeichen-Zeichenfolgen. Ich verstehe, dass es eine Zeichenfolge ist, wenn es wie eine Zeichenfolge quakt. (Hier ist ein Meta-Post darüber, aber möglicherweise gibt es einen relevanteren - oder einen, der meiner Annahme tatsächlich widerspricht.)
Arnauld,
1
Oder anders ausgedrückt: Dies ist so nah wie möglich an einer Liste von Zeichen in JS, die vom OP ausdrücklich zugelassen wurde.
Arnauld
1

Ruby , 104 102 Bytes

->s{l=s.size-1
(0..l).map{|i|n=0
loop{n+=1
(n-i..l-i).all?{|k|k<0||k>=n||s[i+k]==s[i-n+k]}&&break}
n}}

Probieren Sie es online!

Ein Lambda, das eine Zeichenfolge akzeptiert und ein Array zurückgibt.

-2 Byte: Tauschen Sie Bereichsendpunkte mit indexgebundenen Schutzvorrichtungen aus

Ungolfed:

->s{
  l=s.size-1                # l is the maximum valid index into s
  (0..l).map{ |i|           # i is the current index
    n=0                     # n is the period being tested
    loop{                   # Repeat forever:
      n+=1                  # Increment n
      (n-i..l-i).all?{ |k|  # If for all k where i+k and i-n+k are valid indexes into s
        k<0 || k>=n ||      #   We need not consider k OR
          s[i+k]==s[i-n+k]  #   The characters at the relevant indexes match
      } && break            # Then stop repeating
    }
  n                         # Map this index i to the first valid n
  }
}
benj2240
quelle
1

Japt , 33 32 Bytes

1 Byte dank @Shaggy gespeichert

¬Ë@¯E f'$iUtED ú.D r."($&|^)"}aÄ

Online testen!

Erläuterung

¬Ë@¯E f'$iUtED ú.D r."($&|^)"}aÄ   Implicit: U = input string
¬Ë                                 Split the input into chars, and map each index E to
  @                          }aÄ     the smallest positive integer D where
   ¯E                                  the first E chars of U
      f                                matches the regex formed by
          UtED                         taking D chars of U from index E,
                ú.D                     padding to length D with periods,
                    r."($&|^)"          replacing each char C with "(C|^)",
        '$i                             and placing a '$' at the very end.

Mein erster Gedanke war, einfach jedes Zeichen in der linken Teilzeichenfolge mit dem entsprechenden Zeichen in der rechten Teilzeichenfolge zu vergleichen, wie in der JS-Antwort. Das würde jedoch nicht funktionieren, da Japts Methode, um ein Zeichen zu erhalten, nur an das andere Ende der Zeichenkette übergeht, wenn der Index negativ oder zu groß ist.

Stattdessen erstellt meine Lösung einen regulären Ausdruck aus der zweiten Teilzeichenfolge und testet ihn auf der ersten Teilzeichenfolge. Nehmen wir abaabbabals Beispiel das fünfte Objekt im Testfall :

abaabbab
    ^ split point -> abaa for testing regex, bbab for making regex

   slice  regex                              matches abaa
1. b      /(b|^)$/                           no
2. bb     /(b|^)(b|^)$/                      no
3. bba    /(b|^)(b|^)(a|^)$/                 no
4. bbab   /(b|^)(b|^)(a|^)(b|^)$/            no
5. bbab.  /(b|^)(b|^)(a|^)(b|^)(.|^)$/       no
6. bbab.. /(b|^)(b|^)(a|^)(b|^)(.|^)(.|^)$/  yes: /^^ab..$/

Der Haupttrick ist das ^ man unendlich Übereinstimmungen finden kann, bis ein tatsächlicher Charakter gefunden wurde. Auf diese Weise können wir eine beliebige Anzahl von Zeichen vom Beginn der Regex ignorieren und gleichzeitig sicherstellen, dass alle anderen Zeichen nacheinander abgeglichen werden und am Ende der Testzeichenfolge enden.

Ich bin mir nicht sicher, ob ich das sehr gut erklärt habe. Lassen Sie es mich wissen, wenn Sie etwas klarstellen möchten oder etwas anderes, das erklärt werden sollte.

ETHproductions
quelle
32 Bytes .
Shaggy
@ Shaggy Danke, dieses Semikolon hat mich
nervt
1

C (GCC) , 143 142 140 139 128 126 123 Bytes

  • Ein Byte gespeichert. Golfen !b&&printfzu b||printf.
  • Zwei Bytes gespart dank Kevin Cruijssen . Die forLoop-Klammern wurden durch Jonglieren der printfPlatzierung entfernt.
  • Ein Byte gespeichert. Golfen b+=S[i+k]!=S[i-n+k]zu b|=S[i+k]-S[i-n+k].
  • Elf Bytes gespeichert. Es wurde die Notwendigkeit beseitigt l=strlen(S), beide Zeichenkettenbehandlungsschleifen zu konditionieren, um beim Erreichen des Zeichenkettenendes (ein Null-Byte '\0') zu brechen .
  • Zwei Bytes gespeichert. Golf gespielti-n+k>~0 zui-n>~k .
  • Dank ceilingcat drei Bytes gespart ; b||printf("|"),n++ist äquivalent zu n+=b||printf("|").
i,b,k,n;f(char*S){for(i=~0;S[++i];)for(b=n=1;b;n+=b||printf("%d,",n))for(b=k=0;k<n&&S[i+k];k++)b|=n-i>k?0:S[i+k]-S[i-n+k];}

Probieren Sie es online!

Jonathan Frech
quelle
Sie können 2 Bytes sparen, indem Sie die Klammern entfernen und b||printf("%d,",n)die for-Schleife i,b,k,n,l;f(char*S){for(l=strlen(S),i=-1;++i<l;)for(b=n=1;b;b||printf("%d,",n),n++)for(b=k=0;k<n;k++)i+k<l&i-n+k>=0&&(b+=S[i+k]!=S[i-n+k]);}
einfügen
@ KevinCruijssen Vielen Dank.
Jonathan Frech
@ceilingcat Danke; ordentliche Äquivalenz, diese.
Jonathan Frech