Tulis Tokeniser Insiden

24

Latar Belakang

Insiden adalah bahasa pemrograman yang cukup tidak biasa, karena daftar tokennya tidak ditentukan sebelumnya, melainkan disimpulkan dari input. Dengan demikian, melakukan token program Insiden bisa sangat sulit, terutama jika Anda ingin melakukannya secara efisien. Tugas ini adalah tentang melakukan itu sendiri.

Tugas

Program Anda akan diberikan string sebagai input. Berikut adalah algoritma yang digunakan Insiden untuk Tokenise:

  1. Identifikasi semua string yang terjadi sebagai substring input dengan tepat tiga cara (yaitu ada tepat tiga kemunculan string dalam input).
  2. Buang salah satu string yang merupakan substring dari string lain (misalnya untuk input ababab, satu-satunya string yang tersisa adalah ab, bukan aatau b, karena adan bkeduanya merupakan substring dari ab).
  3. Buang string apa pun yang tumpang tindih dalam input. (Misalnya, aaaaberisi tepat tiga salinan aa, tetapi salinan ini tumpang tindih pada karakter kedua dan ketiga, sehingga akan dibuang. Demikian juga, dalam abababa, ada tiga salinan abdan tiga salinan ba, tetapi karakter kedua hingga keenam masing-masing pada tumpang tindih dari a abdan a ba, sehingga keduanya abdan baakan dibuang).
  4. Setiap string yang tersisa pada titik ini adalah token yang digunakan oleh program. Tokenis input asli ke dalam urutan token ini (karena membuang di langkah sebelumnya, hanya akan ada satu cara untuk melakukannya). Setiap karakter dalam input yang bukan bagian dari token apa pun diperlakukan sebagai komentar dan dibuang.

Program Anda harus mengambil string sebagai input, dan mengembalikan tokenation yang sesuai dari string (daftar token, yang masing-masing dinyatakan sebagai string) sebagai output. Selain itu, ini harus dilakukan setidaknya dengan cukup efisien; khususnya, program harus dijalankan dalam waktu kuadratik ("O (n²)") atau lebih baik. (Kebetulan, hampir pasti mungkin lebih cepat daripada kuadrat, tetapi ini bukan , jadi silakan gunakan algoritma tersest yang dapat Anda temukan yang sesuai dengan batasan kompleksitas.)

Klarifikasi

  • Meskipun secara teori program-program Insiden dapat memuat salah satu dari 256 oktet, dapat diterima untuk tujuan tantangan ini bagi program Anda untuk hanya menangani input yang terbentuk dari ASCII yang dapat dicetak (termasuk ruang), ditambah baris dan tab baru. (Semua program Insiden yang diketahui membatasi diri pada bagian ini). Perhatikan bahwa spasi / baris baru / tab tidak spesial dan dapat muncul di tengah-tengah token; Insiden memperlakukan semua 256 oktet sebagai buram.
  • Definisi "kuadrat waktu" adalah "jika ukuran input digandakan, program akan berjalan lebih lambat dengan tidak lebih dari konstanta ditambah faktor 4", yaitu jika t ( x ) adalah waktu maksimum yang diperlukan program Anda untuk proses input ukuran x , maka harus ada beberapa konstanta k sehingga t (2  x ) <4  t ( x ) + k untuk semua x . Ingatlah bahwa membandingkan string membutuhkan waktu sebanding dengan panjang string.
  • Program Anda secara teoritis harus dapat menangani program input dengan panjang berapa pun jika dijalankan dalam varian (kemungkinan hipotetis) dari bahasa Anda yang memiliki memori tidak terbatas dan menggunakan bilangan bulat tak terbatas (tidak apa-apa jika program gagal mencapai tujuan ini ketika dijalankan dalam praktik karena bilangan bulat bahasa atau memori sebenarnya menjadi sangat besar). Anda dapat mengasumsikan (untuk tujuan penghitungan kompleksitas) bahwa bilangan bulat yang tidak lebih besar dari panjang input dapat dibandingkan dalam waktu konstan (walaupun perlu diingat bahwa jika Anda menggunakan nilai yang lebih besar, misalnya karena mengubah input menjadi bilangan bulat tunggal, mereka akan membutuhkan waktu lama untuk membandingkan sebanding dengan jumlah digit yang mereka miliki).
  • Anda dapat menggunakan algoritma apa pun yang sesuai dengan batasan kompleksitas, meskipun itu tidak mengikuti langkah yang sama seperti algoritma yang diposting di atas, asalkan menghasilkan hasil yang sama.
  • Teka-teki ini adalah tentang token input, bukan benar-benar memformat output. Jika cara paling alami untuk menampilkan daftar dalam bahasa Anda melibatkan format yang ambigu (mis. Dipisahkan baris ketika string berisi baris baru literal, atau tanpa pembatas di antara string), jangan khawatir tentang fakta bahwa output berakhir ambigu ( selama daftar sebenarnya dibangun). Anda mungkin ingin membuat versi kedua kiriman Anda yang menghasilkan output yang jelas, untuk membantu dalam pengujian, tetapi versi asli adalah versi yang menghitung untuk penilaian.

Kasus cobaan

Untuk string input berikut:

aaabcbcbcdefdfefedghijghighjkllkklmmmmonono-nonppqpq-pqprsrsrstststuvuvu

program Anda harus menghasilkan daftar keluaran berikut:

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

Kondisi kemenangan

Ini adalah , sehingga program yang paling pendek valid (yaitu perilaku input / output yang benar dan cukup cepat untuk dijalankan), diukur dalam byte, menang.


sumber
Untuk orang-orang yang dapat melihat posting yang dihapus: posting Sandbox ada di sini .
16
Berapa banyak bahasa yang telah Anda buat? ... Tunggu, 35 ?!
Luis Mendo

Jawaban:

14

C (gcc), 324 byte

Fungsi ini fmengambil string yang diakhiri dengan nol dan mencetak token ke stdout. Semua baris baru dapat dihapus dari kode di bawah ini.

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

Versi 376 byte yang lebih lama ini sedikit lebih mudah dibaca; penjelasan di bawah ini berlaku untuk itu.

*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)menghasilkan tabel tuntuk pola puntuk algoritma Knuth – Morris – Pratt. K(c)proses karakter berikutnya cdari string pencarian dan pembaruan m, panjang awalan terbesar pyang dapat ditemukan berakhir pada karakter yang paling baru diproses.

Di forloop pertama , untuk setiap indeks adalam string, kami menghitung berapa kali setiap nilai yang mungkin mterjadi saat mencari di seluruh string untuk mulai substring di a. Kemudian kita mencari yang terbesar lsehingga panjang- lmulai substring aterjadi tepat 3 kali. Jika cukup pendek untuk sepenuhnya berisi string yang ditemukan sebelumnya a, kami mengabaikannya. Jika tumpang tindih, kami menghapus string sebelumnya dari z, array merekam token mana yang akan disimpan. Kalau tidak, panjangnya disimpan di z.

Kemudian, kami menggunakan KMP lagi untuk mencari string untuk token yang direkam z. Jika salah satunya ditemukan di lokasi dengan entri 0 z, kita tahu bahwa token ini dihapus karena tumpang tindih. Jika token tidak dihapus, itu dicetak.

feersum
sumber
1
Apa kompleksitas waktu ini? Harus O(n^2)atau lebih cepat. Dan mengapa ada !!di sana !!z[x-m]?
Yytsi
2
@ TuukkaX Ini persis O (n ^ 2). *Zadalah panjang token berikutnya yang perlu menjadi 0 jika salah satu kejadian token lainnya memiliki nilai 0 pada indeks mereka dalam array, atau menyimpan nilai yang sama jika tidak (dalam hal ini !!z[x-m]harus 1.
feersum
Baik. Tapi saya masih tidak mengerti mengapa !!itu ada di sana. !!xharus tetap x, atau apakah itu memunculkan trik yang tidak saya ketahui?
Yytsi
@ TuukkaX Ya, !!xmembuat xboolean mewakili "kebenarannya". Jadi, !!1 == truedan !!0 == false. Saya tidak tahu C secara khusus, tapi begitulah biasanya
Conor O'Brien
7

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

Berkat @Kritixi Lithos untuk membantu golf kode
Terima kasih kepada @ User2428118 untuk bermain golf 14 byte

(Tidak akan berfungsi di IE7) (Baris baru harus dimasukkan sebagai " \n" dan tab sebagai " \t" dalam string input, karakter unicode apa pun harus dimasukkan sebagai \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``}

Cobalah secara Online

Penjelasan tentang cara kerjanya dan kode ungolfed

Pertama, program ini menghasilkan array Knuth Morris Pratt untuk setiap substring yang memungkinkan;

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

Selanjutnya, program menemukan panjang pencocokan maksimum pada setiap indeks tunggal dalam kata dengan setiap substring. (ini saatnya O (n ^ 2))

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 ini menggunakan data ini untuk menemukan substring terpanjang yang muncul tiga kali untuk setiap karakter awal dalam string.

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 ini menggunakan data ini untuk menghilangkan semua substring yang tidak muncul tepat tiga kali dan semua substring dari substring yang paling lama berlaku.

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

Selanjutnya, program mengatur semua substring yang tumpang tindih atau sebagian untuk dihapus.

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

Untuk setiap nilai yang akan dihapus, semua substring yang setara juga dihapus.

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

Akhirnya, program bergabung dengan array substring bersama dan mengeluarkannya.

fəˈnɛtɪk
sumber
1
Anda memiliki beberapa whiledan ifblok yang hanya memiliki 1 pernyataan di dalamnya. Anda dapat menghapus kawat gigi di {}sekitar pernyataan itu. Misalnya, if(word.charAt(index+i)==word.charAt(index+j)){j++;}dapat menjadiif(word.charAt(index+i)==word.charAt(index+j))j++;
Kritixi Lithos
Saya menggunakan &&s untuk mengganti ifpernyataan, saya memindahkan pernyataan dalam loop sehingga mereka akan berakhir dengan satu pernyataan di bawahnya sehingga saya bisa menghapus kawat gigi. Saya menggunakan terner untuk menggantikan beberapa pernyataan if. Saya memindahkan barang-barang dan berakhir pada 946 byte . Jika Anda tidak mengerti sesuatu yang saya lakukan, jangan ragu untuk bertanya kepada saya :)
Kritixi Lithos
Pada titik ini masalah utama saya dalam bermain golf adalah mencoba memahami apa yang saya tulis di sana. Saya juga tidak tahu optimisasi apa yang dapat saya lakukan untuk bermain golf di javascript.
fəˈnɛtɪk
Apakah Anda ingin membahas ini di ruang obrolan terpisah?
Kritixi Lithos
Disimpan 14 byte .
user2428118