Bir Olay belirteci yazın


24

Arka fon

olay , alışılmadık bir programlama dilidir, çünkü belirteçlerin listesi önceden belirlenmiş değildir, aksine girdilerden çıkarılmıştır. Bu nedenle, bir Olay programını belirtmek, özellikle verimli bir şekilde yapmak istiyorsanız, oldukça zor olabilir. Bu görev kendin yapmakla ilgili.

Görev

Programınıza girdi olarak bir dize verilecektir. İşte Olayı belirtmek için kullandığı algoritma:

  1. Girdinin alt dizisi olarak gerçekleşen tüm dizeleri tam olarak üç şekilde tanımlayın (örn. Bu dizgenin girdi içinde tam olarak üç oluşumu vardır).
  2. Başka tür dize alt dize olan bu dizeleri herhangi atın (örneğin girişi için ababab, geriye kalan tek dize olurdu abdeğil, aya bçünkü ave bher iki altdizgelerin vardır ab).
  3. Girdi içinde çakışan dizeleri atın. (Örneğin, aaaatam olarak üç kopya içerir aa, ancak bu kopyalar ikinci ve üçüncü karakterlerde çakışır, bu nedenle atılır. Benzer şekilde abababa, içeride üç kopya abve üç kopya vardır ba, ancak ikinci ila altıncı karakter bir üst üste abve ba, ikisi de çok abve baatılır) olacaktır.
  4. Bu noktada kalan herhangi bir dize, program tarafından kullanılan belirteçlerdir. Orijinal girişi bu belirteçlerin bir sırasına sokun (önceki adımdaki atma nedeniyle, bunu yapmanın yalnızca bir yolu olacaktır). Girdideki herhangi bir simgenin parçası olmayan karakterler yorum olarak kabul edilir ve atılır.

Programınız girdi olarak bir dize almak ve dizenin karşılık gelen belirtecini (her biri dize olarak ifade edilen belirteçlerin bir listesi) çıktı olarak döndürmelidir. Ek olarak, bu en azından orta derecede verimli yapılmalıdır; özellikle, programın ikinci dereceden ("O (n²)") veya daha iyi çalışması gerekir. (Bu arada, ikinci dereceden daha hızlı gitmek neredeyse kesinlikle mümkün, ama bu değil , bu yüzden karmaşıklık sınırlarına uyan bulabileceğiniz en keskin algoritmayı kullanmaktan çekinmeyin.)

Açıklamalar

  • Her ne kadar Olay programları teorik olarak 256 oktetten herhangi birini içerebilse de, programınızın yalnızca yazdırılabilir ASCII'den (boşluk dahil), ayrıca yeni satırdan ve sekmeden oluşan girişleri işlemesi için bu zorluğun amacı kabul edilebilir. (Bilinen tüm Olay programları kendilerini bu alt kümeyle sınırlandırmaktadır). Space / newline / tab'in özel olmadığını ve belirteçlerin ortasında görünebileceğini unutmayın; Olay, 256 oktetinin tümünü opak olarak kabul ediyor.
  • "İkinci dereceden zaman" tanımı, eğer girişin büyüklüğü iki katına çıkarsa, program sabit artı 4'lük bir faktörden daha fazla yavaş çalışmayacaktır ", yani eğer t ( x ), programınızın azami zamanı boyutu, bir giriş işlemek x , daha sonra bir sabit olmalıdır k , öyle ki t (2  x ) <4  t ( x ) + k tüm x . Dizeleri karşılaştırmanın dizelerin uzunluğu ile orantılı olarak zaman alacağını unutmayın.
  • Programınız, sınırsız hafızası olan ve sınırsız tamsayılar kullanan (muhtemelen varsayımsal) bir dilde çalıştırıldığında, sınırsız belleğe sahip ve sınırsız tamsayılar kullanıyorsa, programın herhangi bir uzunlukta giriş programlarını kullanabilmesi gerekir. dilin tamsayıları veya hafızası aslında oldukça büyüktür). Girişin uzunluğundan daha büyük olmayan tam sayıların, sabit zaman içinde karşılaştırılabileceğini (örneğin, girişi bir cihaza dönüştürmek nedeniyle daha büyük değerler kullanıyorsanız aklınızda bulundurmalısınız) kabul edebilir (karmaşıklığı hesaplama amacıyla). tek bir tamsayı, sahip oldukları basamak sayısıyla orantılı olarak karşılaştırmak için biraz zaman alırlar).
  • Aynı sonuçları ürettiği sürece yukarıda belirtilen algoritma ile aynı adımları izlemese bile karmaşıklık sınırlarına uyan herhangi bir algoritmayı kullanabilirsiniz.
  • Bu yapboz girdiyi biçimlendirme hakkında değil, çıktıyı biçimlendirme hakkında değil. Dilinizde bir liste çıkarmanın en doğal yolu belirsiz bir biçim içeriyorsa (örn. Dizeler değişmez satırlar içerdiğinde veya dizeler arasında sınırlayıcılar olmadan newline ayrılmış), çıktının belirsiz hale gelmesi konusunda endişelenmeyin ( liste aslında yapıldığı sürece). Testinize yardımcı olmak için net çıktı sağlayan ikinci bir versiyon yapmak isteyebilirsiniz, ancak orijinal versiyon puanlama için sayılan versiyondur.

Test durumu

Aşağıdaki giriş dizesi için:

aaabcbcbcdefdfefedghijghighjkllkklmmmmonono-nonppqpq-pqprsrsrstststuvuvu

programınız aşağıdaki çıktı listesini üretmelidir:

a a a bc bc bc d e f d f e f e d gh gh gh k l l k k l pq pq pq u u u

Zafer durumu

Bu , yani bayt cinsinden ölçülen en kısa geçerli (yani doğru girdi / çıktı davranışı ve yürütülmesi için yeterince hızlı) programı kazanır.


Silinen yayınları görebilen kişiler için: Sandbox yayını buradaydı .

16
Kaç dil yarattın? ... Bekle, 35 ?
Luis Mendo,

Yanıtlar:


14

C (gcc), 324 bayt

İşlev fboş sonlandırılmış bir dize alır ve belirteçleri stdout'a yazdırır. Tüm yeni satırlar aşağıdaki koddan kaldırılabilir.

f(char*s){
int n=strlen(s),b=0,z[n-~n],F[n+1],u,a,x=0,l,m,*t=z+n;
int K(i){~m&&s[i]^s[a+m]?m=t[m],K(i):++m;}
for(;b<2*n;){
for(a=b++%n,m=l=-1;a+l<n;K(a+l))t[++l]=m;
for(l=0;l<n;++F[m])K(l++),F[l]=z[a]*=b>n?m^z[a]||~(m=t[z[l-m]]):0;
for(printf("%.*s",z[a],s+a);n/b*l&&a+l>x;l--)F[l]^3?F[t[l]]+=F[l]:(a<x?z[u]=0:(z[u=a]=l),x=a+l);
}
}

Bu eski 376 bayt sürümü okumak için biraz daha kolaydır; Aşağıdaki açıklama bunun için geçerlidir.

*t,m;
char*p;
K(c){for(;~m&&c^p[m];)m=t[m];++m;}
k(i){for(*t=m=-1;p[i];t[++i]=m)K(p[i]);m=0;}
f(char*s){
int n=strlen(s),z[n-~n],F[n+1],u,*Z=z,a=0,x=0,l;
for(t=z+n;a<n;a++){
p=s+a;
for(k(l=z[a]=0);l<n;++F[m])K(s[l++]),F[l]=0;
for(;l&&a+l>x;l--)F[l]^3?F[t[l]]+=F[l]:(a<x?z[u]=0:(z[u=a]=l),x=a+l);
}
for(p=s;*p;printf("%.*s",*Z++,p++))
for(k(x=0);x<n;m==*Z?*Z*=!!z[x-m],m=t[m]:0)
K(s[x++]);
}

k(0)Knuth-Morris – Pratt algoritması tiçin desen tablosu oluşturur p. Arama dizesinin bir K(c)sonraki karakterini ve en son işlenen karakterde biten en büyük önekin uzunluğunu bulabileceğiniz cgüncellemeleri işlemek.mp

İlk fordöngüde, dizgideki her dizin için, başlangıçtaki alt dizginin tüm dizgisinde arama yaparken aher olası değerin mmeydana gelme sayısını sayarız a. Daha sonra en büyük lolanı bulmak için lbaşlangıçtaki uzunluk atam olarak 3 kez gerçekleşir. Tamamen bir önceki için bulunan bir dize içerecek kadar kısa ise a, biz onu görmezden geliyoruz. Çakışırsa, önceki dizgiyi sileceğiz, zbelirteçlerin tutulacağı dizi kaydı. Aksi takdirde, uzunluğu içinde saklanır z.

Ardından, kaydedilen belirteçleri dize aramak için tekrar KMP'yi kullanırız z. İçlerinden biri 0 girişi olan bir yerde bulunursa, zbu belirtecin çakışma nedeniyle silindiğini biliyoruz. Belirteç silinmediyse yazdırılır.


1
Bunun zaman karmaşıklığı nedir? Olmalı O(n^2)ya da daha hızlı olmalı . Ve neden var !!at !!z[x-m]?
Yytsi,

2
@TuukkaX Tam olarak O (n ^ 2). *Zbelirtecin diğer oluşumlarından herhangi birinin dizideki dizinde 0 değeri varsa veya aksi takdirde aynı değeri koruduğunda 0 olması gereken bir sonraki belirtecin uzunluğu (bu durumda 1 !!z[x-m]olmalıdır.
feersum

Peki. Ama neden !!orada olduğunu hala anlamıyorum . !!xhala olmalı mı x, yoksa bilmediğim bir numara mı çağırıyor?
Yytsi

@TuukkaX Eh, "gerçekliğini" temsil eden bir boolean !!xyapar x. Yani !!1 == trueve !!0 == false. Özellikle C bilmiyorum, ama genellikle böyle gider
Conor O'Brien

7

JavaScript, 878 867 842 825 775 752 717 712 704 673 664 650 641 bayt

Golf koduna yardım için @Kritixi Lithos için
teşekkürler 14 byte golf oynamak için @ User2428118 için teşekkürler

(IE7'de çalışmaz) (Yeni satırlar giriş dizesinde " \n" ve sekmesi " \t" olarak girilmeli, herhangi bir unicode karakter olarak girilmelidir \u####)

w=>{for(a=[],b=[],c=[],d=[],f=[],e=[],k=0;k<(g=w.length);a[k++]=h)for(b[R='push']([]),h=[d[k]=f[k]=j=i=0];i++<g-k;){while(j&&w[k+i]!=w[k+j])j=h[j-1];w[k+i]==w[k+j]&&j++,h[R](j)}for(k=0;k<g;k++)for(j=i=0;i<g;i++)if(w[i]!=w[k+j]){while(j&&w[i]!=w[k+j])j=a[k][j-1];w[i]==w[k+j]?i--:b[k][R](j)}else b[k][R](++j);for(k=0;k<g;c[k++]=l){for(h=f.map(Q=>i=l=0);i<g;)h[b[k][i++]]++;for(;i;)h[i]==3?(l=i,i=0):a[k][--i]?h[a[k][i]]+=h[i+1]:0}for(k=0;g>k++;)for(i=0;(S=c[k])&&i<g;)b[k][i++]==S?d[i-S]=S:0;for(k=0;k<g;k++)for(e[R](w.slice(k,(S=d[k])+k)),i=1;i<S;)f[k+i]=1,f[k]|=S<d[k+i]+i++;f.map((X,i)=>(P=e[i],X?e=e.map(Y=>P==Y?"":Y):0));return e.join``}

Çevrimiçi Deneyin

Nasıl çalıştığını ve unungolfed kodunu açıklama

İlk olarak, program her olası alt tabaka için Knuth Morris Pratt dizilerini oluşturur;

for(index=0;index<word.length;index++){
  kmpArray=[0];
  j=0;
  for(i=1;i<word.length-index;i++){
    while(j&&word.charAt(index+i)!=word.charAt(index+j)){
      j=kmpArray[j-1];
    }
    if(word.charAt(index+i)==word.charAt(index+j)){
      j++;
    }
    kmpArray.push(j);
  }
  kmpArrays.push(kmpArray);
}

Daha sonra, program her alt dizinde bulunan kelimedeki her bir dizinde maksimum eşleşme uzunluklarını bulur. (bu O (n ^ 2) zamanıdır)

for(index=0;index<word.length;index++){
  j=0;
  matchLength=[];
  for(i=0;i<word.length;i++){
    if(word.charAt(i)!=word.charAt(index+j)){
      while(j&&word.charAt(i)!=word.charAt(index+j)){
        j=kmpArrays[index][j-1];
      }
      if(word.charAt(i)==word.charAt(index+j)){
        i--;
      }else{
        matchLength.push(j);
      }
    }else{
      j++;
      matchLength.push(j);
      if(j==kmpArrays[index].length){
        j=kmpArrays[index][j-1];
      }
    }
  }
  matchLengths.push(matchLength);
}

Program bu verileri, dizedeki her başlangıç ​​karakteri için üç kez görünen en uzun alt dizeleri bulmak için kullanır.

for(index=0;index<word.length;index++){
  counts=[]
  max=0;
  for(i=0;i<=word.length;i++){
    counts.push(0);
  }
  for(i=0;i<word.length;i++){
    counts[matchLengths[index][i]]++;
  }
  for(i=word.length-1;i>0;i--){
    if(counts[i]==3){
      max=i;
      break;
    }
    if(kmpArrays[index][i-1]){ //if this value has a smaller value it could be as well
      counts[kmpArrays[index][i]]+=counts[i-1];
    }
  }
  maxLengths.push(max);
}

Program, tam olarak üç kez görünmeyen tüm alt dizeleri ve en uzun geçerli alt dizgilerin tüm alt dizgelerini ortadan kaldırmak için bu verileri kullanır.

for(index=0;index<word.length;index++){
  if(!maxLengths[index])
    continue;
  for(i=0;i<word.length;i++){
    if(matchLengths[index][i]==maxLengths[index]){
      tokens[i-maxLengths[index]+1]=maxLengths[index];
    }
  }
}

Daha sonra, program tüm örtüşen veya kısmi alt dizileri kaldırılacak şekilde ayarlar.

for(index=0;index<word.length;index++){
  sStrs.push(word.substring(index,tokens[index]+index));
  for(i=1;i<tokens[index];i++){
    toRemove[index+i]=1;
    if(tokens[index]<tokens[index+i]+i){
      toRemove[index]=1;
    }
  }
}

Kaldırılacak değerlerin her biri için, tüm eşdeğer alt diziler de kaldırılır.

for(index=0;index<word.length;index++){
  if(toRemove[index]){
    removal=sStrs[index];
    for(i=0;i<3;i++){
      indxOf=sStrs.indexOf(removal);
      sStrs[indxOf]="";
      toRemove[indxOf]=0;
    }
  }
}

Son olarak, program alt dizilerin dizisini birleştirir ve çıkarır.


1
İçinde yalnızca 1 ifade olan bazılarınız whileve ifbloklarınız var. Bu {}ifadenin etrafındaki parantezleri kaldırabilirsiniz . Örneğin, if(word.charAt(index+i)==word.charAt(index+j)){j++;}olabilirif(word.charAt(index+i)==word.charAt(index+j))j++;
Kritixi Lithos,

İfadeleri &&değiştirmek için s'yi kullandım if, ifadeleri döngüler içinde taşıdım, böylece altlarında bir ifade bırakacaklardı, böylece ayraçları kaldırabilirdim. Bazı if ifadelerini değiştirmek için üçlü harf kullandım. Eşyalarımı oynattım ve 946 baytta bitirdim . Yaptığım bir şeyi anlamıyorsan, bana sormaya çekinmeyin :)
Kritixi Lithos

Bu noktada golf ile ilgili temel problemim orada ne yazdığımı anlamaya çalışmak. Javascript'te golf oynamak için ne gibi optimizasyonlar yapabileceğimi de bilmiyorum.
fənɛtɪk

Bunu ayrı bir sohbet odasında görüşmek ister misiniz?
Kritixi Lithos,

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.