Напишіть маркер маркетингу


24

Фон

Інцидент є досить незвичною мовою програмування, оскільки його перелік лексем не заздалегідь визначений, а виводиться із вхідних даних. Таким чином, маркування програми для інцидентів може бути досить важким, особливо якщо ви хочете зробити це ефективно. Це завдання полягає в тому, щоб зробити це самостійно.

Завдання

Вашій програмі буде надано рядок як вхід. Ось алгоритм, який Incident використовує для його маркування:

  1. Визначте всі рядки, які виникають як підрядку вводу рівно трьома способами (тобто є точно три входження цього рядка у вхід).
  2. Відмовтеся від будь-якого з цих рядків , які є підрядками інший такого рядка (наприклад , для введення ababab, єдиними залишилася рядок буде ab, чи не aчи b, тому що aі bобидва підрядка ab).
  3. Відмовтеся від будь-яких рядків, які перекриваються вхідними даними. (Наприклад, aaaaміститься рівно три примірники aa, але ці копії перекриваються на другому та третьому символах, тому вони будуть відкинуті. Так само, у abababa, є три копії abта три копії ba, але другий-шостий символи є кожним у перекриття a abі a ba, тому і те, abі baбуло б відкинуто).
  4. Будь-які рядки, які залишаються на цьому етапі, - це лексеми, які використовує програма. Токенізуйте оригінальний вхід у послідовність цих лексем (у зв'язку з відкиданням на попередньому етапі це буде лише один спосіб зробити це). Будь-які символи вхідних даних, які не входять до жодного маркера, трактуються як коментарі та відкидаються.

Ваша програма повинна взяти рядок як вхідний файл і повернути відповідне маркерування рядка (список жетонів, кожен з яких виражається у вигляді рядків) як вихід. Крім того, це потрібно зробити хоча б помірно ефективно; конкретно, програма повинна запускатися в квадратичний час ("O (n²)") або краще. (До речі, майже напевно можливо пройти швидше, ніж квадратичний, але це не , тому сміливо використовуйте найпростіший алгоритм, який ви можете знайти, що відповідає рамкам складності.)

Роз'яснення

  • Хоча програми інцидентів теоретично можуть містити будь-який із 256 октетів, для цієї програми прийнятно для вашої програми обробляти лише входи, сформовані з друкованого ASCII (включаючи пробіл), а також новий рядок та вкладку. (Усі відомі програми інцидентів обмежуються цим підмножиною). Зауважте, що пробіл / нова лінія / вкладка не є спеціальними і можуть з’являтися посередині лексем; Інцидент трактує всі 256 октетів як непрозорі.
  • Визначенням "квадратичного часу" є "якщо розмір вводу подвоїться, програма буде працювати повільніше, не більше ніж константа плюс коефіцієнт 4", тобто якщо t ( x ) - це максимальний час, який ваша програма займає обробляти вхід розміром x , тоді повинна бути деяка константа k така, що t (2  x ) <4  t ( x ) + 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 ?!
Луїс Мендо

Відповіді:


14

C (gcc), 324 байт

Функція fзаймає нульовий завершений рядок і друкує маркери для 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)створює таблицю tдля шаблону pдля алгоритму Knuth – Morris – Pratt. K(c)обробляти наступний символ cрядка пошуку та оновлень m, довжина найбільшого префіксу pяких можна знайти, що закінчується самим нещодавно обробленим символом.

У першому forциклі, для кожного індексу aв рядку, ми підраховуємо кількість разів, mколи виникає кожне можливе значення при пошуку у всій рядку для підрядка, що починається зa . Тоді ми шукаємо найбільшу lтаку, що довжина lпідстрочки починалася aрівно в 3 рази. Якщо він досить короткий, щоб повністю містити рядок, знайдений для попереднього a, ми ігноруємо його. Якщо вона перекривається, ми видаляємо попередній рядок із zзапису масиву, який буде зберігатися. В іншому випадку його довжина зберігається в z.

Потім ми знову використовуємо KMP для пошуку рядка для записаних жетонів z. Якщо один із них знайдений у місці, в якому є 0z , ми знаємо, що цей маркер був видалений через перекриття. Якщо маркер не був видалений, він надрукується.


1
Яка часова складність цього? Доводиться O(n^2)чи швидше. А чому там !!на !!z[x-m]?
Yytsi

2
@TuukkaX Це точно O (n ^ 2). *Z- це довжина наступного маркера, яка повинна дорівнювати 0, якщо будь-яке з інших подій токена має значення 0 у своєму індексі в масиві, або зберігати таке ж значення в іншому випадку (у такому випадку !!z[x-m]має бути 1.
feersum

Добре. Але я досі не розумію, чому !!це існує. !!xвсе-таки має бути x, чи це викликає витівку, про яку я не знаю?
Yytsi

@TuukkaX Добре !!xробить буле, xщо представляє його "правдивість". Отже, !!1 == trueі !!0 == false. Я конкретно не знаю С, але так зазвичай йде
Конор О'Браєн

7

JavaScript, 878 867 842 825 775 752 717 712 704 673 664 650 641 байт

Дякуємо @Kritixi Lithos за те, що він допомагає гольф-коду.
Завдяки @ User2428118 за теніс на гольфі на 14 байт

(Не буде працювати в IE7) (Нові рядки слід вводити як " \n", а вкладку як " \t" у рядок введення; будь-які символи unicode слід вводити як \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
У вас є деякі whileі ifблоки , які мають тільки 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 байт . Якщо ви не розумієте чогось, що я зробив,
сміливо

На даний момент моя головна проблема з гольфу - це намагання зрозуміти, що я там написав. Я також не знаю, які оптимізації я можу зробити для гольфу в javascript.
fəˈnɛtɪk

Ви хочете обговорити це в окремій чаті?
Kritixi Lithos

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.