Palindrome Sıkıştırma


15

Meydan okuma

ASCII metnini kayıpsız bir şekilde sıkıştıran ve açılan bir program yazın. Büyük / küçük harf duyarsız ve noktalama işaretine duyarlı olmayan palindromlar da dahil olmak üzere palindromlarla iyi çalışacak şekilde uzmanlaştırılmalıdır. En küçük kaynakla en iyi sıkıştırma kazanır.

puanlama

total_bytes_saved / sqrt(program_size) - En yüksek puan kazanır

total_bytes_savedsıkıştırılmış dizelerin orijinallerinden kaç bayt kadar küçük olduğu, aşağıdaki test senaryoları toplamıdır. program_sizehem sıkıştırma hem de açma programlarının kaynak kodunun bayt cinsinden boyutudur. İkisi arasında paylaşılan kodun yalnızca bir kez sayılması gerekir.

Örneğin, 10 test vakası ve 100 bayt programı 7 test vakasında 5 bayt kaydedilmiş, her birinde 2'si 10 bayt olsaydı, ancak son test vakası 2 bayt daha uzun olsaydı, çözüm 5.3 puan alacaktı. ( (7 * 5 + 10 * 2 - 2) / sqrt(100) = 5.3)

Test Durumları

  • tacocat
  • toohottohoot
  • todderasesareddot
  • amanaplanacanalpanama
  • wasitacaroracatisaw?
  • Bob
  • IManAmRegalAGermanAmI
  • DogeeseseeGod
  • A Santa at NASA
  • Go hang a salami! I'm a lasagna hog.

kurallar

  1. Standart boşluklar geçerlidir.
  2. Sıkıştırma yalnızca palindromlar üzerinde değil tüm yazdırılabilir ASCII (bayt 32-126, dahil) metin dizelerinde çalışmalıdır. Bununla birlikte, aslında herhangi bir giriş için yer tasarrufu yapmak zorunda değildir.
  3. Çıktı, uygulanması veya dahili temsili ne olursa olsun bayt veya karakter dizisi olabilir (örneğin, dizeler, listeler ve dizilerin tümü adil oyundur). UTF-8 kodlaması yapılıyorsa karakterleri değil, baytları sayın. Geniş dizelere (örn. UTF-16 veya UTF-32) izin verilen tek kod noktaları 0 ile 255 arasında olmadığı sürece izin verilmez.
  4. Sıkıştırma / Açma yerleşiklerine izin verilmez.

Kendi zevkimiz için, sıkıştırılmış dizeleri kaynak kodunuzla birlikte gönderin.

GÜNCELLEME 1: Daha iyi sıkıştırmaya daha fazla ağırlık vermek ve agresif golf oynamaya daha az ağırlık vermek total_bytes_saved / program_sizeiçin puanlama değeri olarak değiştirildi total_bytes_saved / sqrt(program_size). Puanlarınızı buna göre ayarlayın.

GÜNCELLEME 2: Sabit wasitacaroraratisaw?olmakwasitacaroracatisaw?


2
Durum, noktalama işaretleri ve aralıklar girişten kaldırılırsa, girişlerin katı palindromlar olacağı garanti edilir mi? Edit: nevermind - Ben wasitacaroraratisaw?bunun bir karşı örnek olduğunu görüyorum
Dijital Travma

2
Girişte hangi ASCII karakterlerini desteklememiz gerekiyor? Öyle mi [32-126]?
Arnauld

1
Evet, 1000 *parçanın gerçekten gerekli olduğunu düşünmüyorum ve hayır puanın daha "tatmin edici" hissettireceğini sanmıyorum;)
Outgolfer Erik

1
Sıkıştırma / dekompresyon yerleşiklerini kullanabilir miyiz?
Lynn

4
Bu kadar az girdi olduğunda, akıllıca bir şey yapmak için çok fazla alan yoktur. En az birkaç kez daha olması güzel olurdu.
user1502040

Yanıtlar:


16

JavaScript (ES6), 3.143 (81 bayt kaydedildi, 664 bayt programı)

R='replace',S=String.fromCharCode,T=c=>c.charCodeAt(),U='toUpperCase',V='0000000',W=(a,b,c=2)=>a.toString(c).slice(b),X=x=>'0b'+x,Y=a=>[...a].reverse().join``,Z=/[^]/g
C=s=>S(...((Y(q=s[U]()[R](/[^A-Z]/g,m=''))==q?(q=q.slice(0,p=-~q.length/2),p%1&&10):11)+q[R](Z,x=>W(T(x),2))+111+s[R](Z,c=>/[a-z]/.test(c)?W("00",m,m=1):m+(/[A-Z]/.test(c,m='')?"01":W(c<'!'?2:T(c)+384)))+V).match(/(?!0+$).{8}/g).map(X))
D=s=>{s=s[R](Z,c=>W(256+T(c),1))+V;M=r=>(s=s[R](p=s.match(`^${r}|`)[0],''),p);for([,a]=M`1.|0`,t=u=i='';!M`111`;)t+=W(X(M`.{5}`)-~8,0,36);for(t+=W(Y(t),a?a/0:1);p;)u+=M`0(?=00)|00?1`?(c=t[i++])?+p[1]?c[U]():c:'':M`10`?' ':M`11`&&S(X(M`.{7}`));return u+W(t,i)}

Şimdi bu programdan (ve puanlama sisteminden) oldukça memnun olduğuma göre, biraz açıklama yazacağım.

Temel fikir, girdiyi bir bit dizisine sıkıştırmak, ardından 8 bitlik her bir seti bir bayta sıkıştırmaktır. Açıklama amacıyla, sadece bit dizesini değiştireceğim.

Bit dizesi birkaç bölüme ayrılabilir:

input  -> Taco Cat.
output -> 0101000000100011011111110100001100100011101011100000000

0      | 10100 00001 00011 01111 111 | 01 00001 10 01 0001 110101110
header | letter data                 | styling data

Başlık çok basit bir eşlemedir:

0  -> odd-length palindrome
10 -> even-length palindrome
11 -> non-palindrome

Mektup verileri de oldukça basittir. İlk olarak, tüm harf olmayanlar dizeden çıkarılır ve tüm harfler büyük harfe dönüştürülür. Elde edilen dize bir palindromsa, ters yarı soyulur. Ardından bu eşleme uygulanır:

A -> 00001
B -> 00010
C -> 00011
D -> 00100
...
Z -> 11010

Bu bölüm ile sonlandırılır 111. Bundan sonra, büyük / küçük harf verilerini ve harf olmayanları depolayan stil verileri gelir. Bu şu şekilde çalışır:

01 -> next letter as uppercase
0...01 (n 0s) -> next (n-1) letters as lowercase
10 -> space
11xxxxxxx -> character with code point 0bxxxxxxx

Yukarıda gösterilen örneğe baktığımızda,

header: 0 -> palindrome
letter data: 10100 00001 00011 01111 111 -> taco
styling data:
  01        -> T
  00001     -> aco
  10        -> <space>
  01        -> C
  0001      -> at
  110101110 -> .

Bit dizesinin sonuna ulaşıldığında, harf verilerinden kalan tüm karakterler sonuca eklenir. Bu bizi sonuncusu yapmaktan kurtarır 000...001ve bu bitleri dizeden kesmemize izin verir.

Test senaryolarının gözden geçirilmesi:

tacocat -> 3 bytes (-4)
    24 bits: 010100000010001101111111
toohottohoot -> 5 bytes (-7)
    35 bits: 10101000111101111010000111110100111
todderasesareddot -> 7 bytes (-10)
    49 bits: 0101000111100100001000010110010000011001100101111
amanaplanacanalpanama -> 8 bytes (-13)
    59 bits: 00000101101000010111000001100000110000001011100000100011111
wasitacaroracatisaw? -> 11 bytes (-9)
    84 bits: 010111000011001101001101000000100011000011001001111111000000000000000000001110111111
Bob -> 2 bytes (-1)
    16 bits: 0000100111111101
IManAmRegalAGermanAmI -> 13 bytes (-8)
    98 bits: 00100101101000010111000001011011001000101001110000101100111010100010100101000001010100000010100101
DogeeseseeGod -> 7 bytes (-6)
    54 bits: 000100011110011100101001011001100101111010000000000101
A Santa at NASA -> 8 bytes (-7)
    63 bits: 100000110011000010111010100000011110110010000011000011001010101
Go hang a salami! I'm a lasagna hog. -> 20 bytes (-16)
   154 bits: 1000111011110100000001011100011100001100110000101100000010110101001111010011000000110001100000000111010000110011101001110011000110000000001100000111010111

Vay. Bu yaklaşımdan gerçekten çok etkilendim. Böyle bir bit kodlaması yapmayı hiç düşünmezdim. (ASCII'yi 7 bit olarak paketlemeyi düşündüm, ancak palindromlar için fazla yer tasarrufu yapmadım) Yerden de tasarruf etmeyi başardığınızdan çok etkilendim Bob.
Beefster

4
Bu, mühendisliğin temellerine harika bir örnektir. Bir problem açıklaması almak, çözmek için farklı yollar düşünmek ve gereksinimler arasında (örneğin çeşitli stillere
Robert Fraser

@Beefster Teşekkürler :-) Bobgerçekten yerine düştü - başlık için 1 bit, iki harf için 10 + 3 bit ve tek büyük harf için 2 bit. Benim en zor denedim daha kısa
olamazdı

1
@KevinCruijssen sorun, eklenen bir dize olmasıdır, bu yüzden önce bir sayıya dönüştürmek zorunda. Bu yol bayttan daha kısadır-0+9
ETHproductions

1
@ETHproductions Ah tabii (bir dize olduğunu fark etmedi)! +9dizeye bağlanırken aritmetik olarak -~8yapardı +9(çünkü -dizeler için hiçbir şey yapmaz, bu yüzden onu sayı olarak yorumlar). Bu durumda -~8oldukça zekidir. :) Güzel cevap btw, benden +1! Çok akıllıca tüm bilgileri böyle bir bitte saklamak, hatta bir bayt tasarrufu yapmak Bob.
Kevin Cruijssen

2

Python 2: 2.765 (70 bayt kaydedildi, 641 bayt programı)

Yaklaşımımı biraz değiştirdim. Artık kusurlu palindromlarda iyi çalışıyor. Girişten daha uzun olacak sıkıştırılmış dizeler yoktur. Mükemmel çift uzunluklu palindromlar her zaman orijinal boyutun% 50'sine sıkıştırır.

A=lambda x:chr(x).isalpha()
def c(s):
 r=bytearray(s);q=len(r);L=0;R=q-1;v=lambda:R+1<q and r[R+1]<15
 while L<=R:
  while not A(r[L])and L<R:L+=1
  while not A(r[R])and R:
   if v()and r[R]==32:r[R]=16+r.pop(R+1)
   R-=1
  j=r[L];k=r[R]
  if A(j)*A(k):
   if L!=R and j&31==k&31:
    r[L]+=(j!=k)*64;r[R]=1
    if v():r[R]+=r.pop(R+1)
   else:r[L]|=128;r[R]|=128
  L+=1;R-=1
 while r[-1]<16:r.pop()
 return r
def d(s):
 r='';t=[]
 for o in s:
  if 15<o<32:r+=' ';o-=16
  while 0<o<16:r+=chr(t.pop());o-=1
  if o==0:continue
  if 127<o<192:o-=64;t+=[o^32]
  elif o>192:o-=128
  elif A(o):t+=[o]
  r+=chr(o)
 while t:r+=chr(t.pop())
 return r

Sonuçlar

'tacocat' <==> 'tac\xef'
4/7 (3 bytes saved)
'toohottohoot' <==> 'toohot'
6/12 (6 bytes saved)
'todderasesareddot' <==> 'todderas\xe5'
9/17 (8 bytes saved)
'amanaplanacanalpanama' <==> 'amanaplana\xe3'
11/21 (10 bytes saved)
'wasitacaroracatisaw?' <==> 'wasita\xe3ar\xef\x09?'
12/20 (8 bytes saved)
'Bob' <==> '\x82\xef'
2/3 (1 bytes saved)
'IManAmRegalAGermanAmI' <==> 'I\x8d\xa1n\x81m\x92e\xa7\xa1\xec'
11/21 (10 bytes saved)
'Dogeeseseegod' <==> '\x84ogees\xe5'
7/13 (6 bytes saved)
'A Santa at NASA' <==> 'A S\xa1\xaeta\x12\x14'
9/15 (6 bytes saved)
"Go hang a salami! I'm a lasagna hog." <==> "\x87o hang a salam\xa9!\x11'\x01\x11\x17\x13."
24/36 (12 bytes saved)

Ve bir bonus olarak, daha önce sahip olduğum yanlış palindromuma 6 bayt kaydediyor.

'wasita\xe3ar\xef\x02\xf2\x06?' <==> 'wasitacaroraratisaw?'
6 bytes saved

açıklama

Dekompresyon bir yığın kullanır. 32-127 kod noktaları tam anlamıyla ele alınır. Bir karakter bir harfse, yığına bir değer de itilir. Büyük harf döndürülmüş harfler için 128-192 değerleri kullanılır, böylece büyük harf döndürülmüş harf (ASCII'nin o^32nasıl düzenlendiği nedeniyle) yığına itilir ve dizeye normal harf eklenir. Değerler 192-255, yığına itmeden harfler eklemek için kullanılır, bu nedenle harfler eşleşmediğinde ve tek uzunlukta palindromlardaki orta harf için kullanılır. 1-15 arasındaki kod noktaları, yığının bu sayıda atanması gerektiğini belirtir. Kod noktaları 17-31 benzerdir, ancak yığından çıkmadan önce bir boşluk yazdırırlar. Bir girişin sonunda örtük bir "boşa kadar pop" talimatı da vardır.

Kompresör her iki uçtan da çalışır ve eşleşen harfler 1-31 arasında katlanır. Harf olmayan harflerin üzerinden atlar. Harfler eşleştiğinde, ancak büyük / küçük harf eşleşmediğinde, sol harfe 64 ekler ve sağ harfi arttırır. Bu yerden tasarruf etmesini sağlar IManAmRegalAGermanAmI. Ortada veya harfler eşleşmediğinde, her iki tarafa da 128 olur. Oraya ekleyemem çünkü özel durumdan kaçınmam gerekiyor left == right. Sağ taraftaki komşu pop işaretleyicileri katlarken, komşu olan kod noktası 16'ya taşmayacağını kontrol etmeliyim çünkü boşluklar için buna ihtiyacım var. (Bu aslında hiçbir test senaryosu dizesi için bir sorun değildir)

DÜZENLEME 1 : Artık çözülmemiş sürüm yok.


1

Python3, 1.833 (25 bayt kaydedildi, 186 bayt programı)

Sadece basit 0-sıralı eşit olasılıklı entropi kodlaması. Palindrom'a özgü optimizasyon yok.

def C(s):
    u=0
    for c in s:u=u*96+ord(c)-31
    return u.to_bytes((u.bit_length()+7)//8,'big')
def D(a):
    u,s=int.from_bytes(a,'big'),''
    while u:s,u=s+chr((u%96)+31),u//96
    return s[::-1]

0

Java 8, puan: 1.355 (20 bayt kaydedildi / 218 (107 + 111) bayt)

Sıkıştırma işlevi (yazdırılamaz üç ASCII karakteri içerir):

s->{int l=s.length();return s.contains(new StringBuffer(s).reverse())?s.substring(l/2)+(l%2<1?"":""):s;}

Sıkıştırmayı açma işlevi (yazdırılamaz iki ASCII karakteri içerir):

s->{return s.contains("")?new StringBuffer((s=s.replaceAll("","")).substring(s.length()&1^1)).reverse()+s:s;}

Açıklama:

Çevrimiçi deneyin.

Sadece mükemmel palindromları sıkıştırır.

s->{                      // Method with String as both parameter and return-type
  int l=s.length();       //  Get the length of the input
  return s.contains(new StringBuffer(s).reverse())?
                          //  If the input is a palindrome:
    s.substring(l/2)      //   Only return the second halve of the String
    +(l%2<1?"":"")        //   + either one (if even) or two (if odd) unprintables 
   :                      //  Else:
    s;}                   //   Simply return the input again

s->{                      // Method with String as both parameter and return-type
  return s.contains("")?  //  If the input contains an unprintable:
    new StringBuffer((s=s.replaceAll("",""))
                          //   Remove the unprintables
                     .substring(s.length()&1^1))
                          //   And take either the full string (if even),
                          //   or minus the first character (if odd)
    .reverse()            //    And reverse that part
    +s                    //   And append the rest of the input (minus the unprintables)
   :                      //  Else:
    s;}                   //   Simply return the input again
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.