인시던트 토큰 화기 작성


24

배경

인시던트 는 토큰 목록이 미리 결정되지 않고 입력에서 유추된다는 점에서 상당히 특이한 프로그래밍 언어입니다. 따라서 인시던트 프로그램을 토큰 화하는 것은 특히 어려울 수 있습니다. 이 작업은 직접 수행하는 것입니다.

작업

프로그램에 문자열이 입력됩니다. 사건이이를 토큰 화하는 데 사용하는 알고리즘은 다음과 같습니다.

  1. 입력의 하위 문자열로 발생하는 모든 문자열을 정확히 세 가지 방법으로 식별하십시오 (예 : 입력 내에 해당 문자열이 정확히 3 개 발생 함).
  2. 이러한 다른 스트링의 스트링 이들 문자열 중 하나를 폐기 (예 : 입력 ababab유일한 잔여 문자열 것이 ab아니라 ab때문에, a그리고 b두 문자열이다 ab).
  3. 입력 내에서 겹치는 문자열을 버리십시오. (예를 들어, aaaa정확히 세 개의 사본이 포함되어 aa있지만이 사본은 두 번째와 세 번째 문자에서 겹치므로 폐기됩니다. 마찬가지로에서의 abababa세 개의 사본 ab과 세 개의 사본이 ba있지만 두 번째에서 여섯 번째 문자는 각각 의 오버랩 ab하고 ba, 모두 너무 ab하고 ba) 폐기 될 것이다.
  4. 이 시점에 남아있는 문자열은 프로그램에서 사용하는 토큰입니다. 원래 입력을 이러한 토큰의 순서로 토큰 화하십시오 (이전 단계에서의 폐기로 인해 한 가지 방법 만있을 수 있습니다). 입력에 토큰의 일부가 아닌 문자는 주석으로 처리되어 버려집니다.

프로그램은 문자열을 입력으로 취하고 문자열의 해당 토큰 화 (각각 문자열로 표시되는 토큰 목록)를 출력으로 리턴해야합니다. 또한, 이것은 적어도 적당히 효율적으로 수행되어야합니다. 특히 프로그램은 2 차 시간 ( "O (n²)") 이상 으로 실행해야합니다 . ( 실수로 2 차보다 빠를 가능성이 거의 있지만 이것이 은 아니므로 복잡성 범위에 맞는 가장 정확한 알고리즘을 자유롭게 사용하십시오.)

설명

  • 이론적으로 인시던트 프로그램에는 256 옥텟이 포함될 수 있지만 프로그램이 인쇄 가능한 ASCII (공백 포함)로 구성된 입력과 줄 바꿈 및 탭만 처리하는 것은 이러한 과제의 목적으로 허용됩니다. 알려진 모든 인시던트 프로그램은이 하위 집합으로 제한됩니다. space / newline / tab은 특별하지 않으며 토큰 중간에 나타날 수 있습니다. 인시던트는 모든 256 옥텟을 불투명으로 처리합니다.
  • "이차 시간"의 정의는 "입력의 크기가 두 배가되면, 프로그램은 상수에 4를 더한 값만큼 느리게 실행됩니다", 즉 t ( x )가 프로그램에 걸리는 최대 시간 인 경우 크기 x 의 입력을 처리하면 t (2  x ) <4  t ( x ) + k 와 같은 일정한 k 가 있어야합니다. 모든 X . 줄을 비교하는 것은 줄의 길이에 비례하여 시간이 걸린다는 것을 명심하십시오.
  • 메모리가 무제한이고 무제한 정수를 사용하는 언어의 (가상적인) 변형 언어로 실행하는 경우 이론적으로 프로그램은 길이에 상관없이 입력 프로그램을 처리 할 수 ​​있어야합니다 (실제로 실행할 때 프로그램이이 목표를 달성하지 못하면 괜찮습니다) 언어의 정수 또는 메모리가 실제로 유한하게 크다). 입력의 길이보다 크지 않은 정수는 일정한 시간에 비교할 수 있다고 (복잡도 계산을 위해) 가정 할 수 있습니다 (물론 큰 값을 사용하면 입력을 단일 정수의 경우 자릿수에 비례하여 비교하는 데 시간이 걸립니다).
  • 동일한 결과를 생성하는 한 위에 게시 된 알고리즘과 동일한 단계를 따르지 않더라도 복잡성 범위 내에 맞는 알고리즘을 사용할 수 있습니다.
  • 이 퍼즐은 실제로 출력 형식을 지정하는 것이 아니라 입력을 토큰 화하는 것에 관한 것입니다. 언어로 목록을 출력하는 가장 자연스러운 방법이 모호한 형식 (예 : 문자열에 리터럴 개행 문자가 있거나 문자열 사이에 구분자가없는 경우 줄 바꿈으로 구분됨)이있는 경우 출력이 모호하다는 사실에 대해 걱정하지 마십시오 ( 목록이 실제로 구성된 한). 테스트를 돕기 위해 모호하지 않은 결과물을 생성하는 두 번째 버전의 제출물을 만들 수 있지만 원래 버전은 점수를 계산하는 버전입니다.

테스트 사례

다음 입력 문자열의 경우 :

aaabcbcbcdefdfefedghijghighjkllkklmmmmonono-nonppqpq-pqprsrsrstststuvuvu

프로그램은 다음과 같은 출력 목록을 생성해야합니다.

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

승리 조건

이것은 이므로 바이트 단위로 측정 된 가장 짧은 유효한 (즉, 올바른 입력 / 출력 동작 및 충분히 빠른 실행) 프로그램이 승리합니다.


삭제 된 게시물을 볼 수있는 사용자의 경우 : 샌드 박스 게시물이 여기 있었습니다.

16
얼마나 많은 언어를 만들었습니까? ... 잠깐, 35 ?!
Luis Mendo

답변:


14

C (gcc), 324 바이트

이 함수 f는 null로 끝나는 문자열을 가져 와서 토큰을 stdout에 인쇄합니다. 아래 코드에서 모든 줄 바꿈을 제거 할 수 있습니다.

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

이 오래된 376 바이트 버전은 조금 더 읽기 쉽습니다. 아래의 설명이 적용됩니다.

*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 알고리즘의 t패턴에 p대한 테이블 을 생성합니다 . 검색 문자열 K(c)의 다음 문자 c를 처리 하고 업데이트 m합니다. 가장 큰 접두어의 길이 p는 가장 최근에 처리 된 문자로 끝나는 것을 알 수 있습니다.

처음에는 for 루프 a에서 문자열의 각 인덱스 m에 대해 전체 문자열에서 시작하는 하위 문자열을 검색 할 때 가능한 각 값이 발생 하는 횟수를 계산합니다 .a . 그런 다음 시작 부분 l길이 l문자열 a이 정확히 3 번 발생 하는 최대 값을 찾습니다 . 이전에 찾은 문자열에 완전히 포함되기에 충분히 짧은 경우 a무시합니다. 겹치면 z토큰을 유지할 배열 기록 에서 이전 문자열을 삭제합니다 . 그렇지 않으면 길이가에 저장됩니다 z.

그런 다음 KMP를 다시 사용하여에 기록 된 토큰을 문자열에서 검색합니다 z. 그중 하나가 0 항목이있는 위치에서 발견되면z 에서이 되면이 토큰이 중복되어 삭제 된 것입니다. 토큰이 삭제되지 않은 경우 인쇄됩니다.


1
이것의 시간 복잡성은 무엇입니까? O(n^2)이상 이어야 합니다. 그리고 왜 거기 !!!!z[x-m]있습니까?
Yytsi

2
@TuukkaX 정확히 O (n ^ 2)입니다. *Z다음 토큰의 길이는 토큰의 다른 어커런스가 배열의 인덱스 값이 0 인 경우 0이되거나 그렇지 않으면 같은 값을 유지합니다 (이 경우 1 !!z[x-m]이어야 함).
feersum

좋구나. 그러나 나는 왜 !!거기에 있는지 이해하지 못합니다 . !!x여전히이어야 x합니까, 아니면 내가 모르는 트릭을 호출합니까?
Yytsi

@TuukkaX 글쎄, "진실"을 나타내는 부울을 !!x만듭니다 x. 그래서, !!1 == true하고 !!0 == false. 나는 C를 구체적으로 알지 못하지만, 그것이 일반적으로 진행되는 방식입니다
Conor O'Brien

7

자바 스크립트, 878 867 842 825 775 752 717 712 704 673 664 650 641 바이트

골프를 도와주는 @Kritixi Lithos에게
감사합니다. 14 바이트의 골프를 치르는 @ User2428118에게 감사드립니다

(IE7에서는 작동하지 않음) ( 입력 문자열에서 줄 바꿈을 " \n" 로 입력 하고 탭 " \t"을 입력해야하며 유니 코드 문자는으로 입력해야합니다. \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``}

온라인으로 사용해보십시오

작동 방식 및 골프화되지 않은 코드에 대한 설명

먼저, 프로그램은 가능한 모든 부분 문자열에 대해 Knuth Morris Pratt 배열을 생성합니다.

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

다음으로, 프로그램은 각 하위 문자열이있는 단어의 모든 단일 색인에서 최대 일치 길이를 찾습니다. (이것은 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);
}

프로그램은이 데이터를 사용하여 문자열에서 각 시작 문자에 대해 세 번 나타나는 가장 긴 하위 문자열을 찾습니다.

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

프로그램은이 데이터를 사용하여 정확히 세 번 나타나지 않는 모든 부분 문자열과 가장 긴 유효한 부분 문자열의 모든 부분 문자열을 제거합니다.

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

다음으로, 프로그램은 모든 겹치거나 부분 서브 스트링이 제거되도록 설정합니다.

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

제거 할 각 값에 대해 동등한 하위 문자열도 모두 제거됩니다.

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

마지막으로 프로그램은 하위 문자열 배열을 결합하여 출력합니다.


1
당신은 몇 가지가 whileif그 중 1 문 안쪽에있는 블록을. {}해당 문장 주위 의 괄호를 제거 할 수 있습니다 . 예를 들어 다음과 같이 if(word.charAt(index+i)==word.charAt(index+j)){j++;}될 수 있습니다if(word.charAt(index+i)==word.charAt(index+j))j++;
Kritixi Lithos

나는 &&s를 사용 하여 if명령문 을 대체하고 명령문을 루프로 이동하여 중괄호를 제거 할 수 있도록 그 아래에 하나의 명령문으로 끝날 수 있도록했습니다. 삼항을 사용하여 일부 if 문을 대체했습니다. 나는 물건을 옮기고 946 바이트로 끝났습니다 . 내가 한 일을 이해하지 못한다면 언제든지 물어보십시오. :)
Kritixi Lithos

이 시점에서 골프에 관한 나의 주요 문제는 내가 거기에 쓴 것을 이해하려고 노력하고 있습니다. 또한 자바 스크립트에서 골프를 위해 어떤 최적화를 할 수 있는지 모르겠습니다.
fəˈnɛtɪk

별도의 대화방에서이 문제에 대해 토론하고 싶습니까?
Kritixi Lithos

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.