Якого типу мої суфікси?


10

Вступ

Тож я знову витрачаю час на дослідження алгоритмів сортування суфіксів, оцінювання нових ідей вручну та в коді. Але я завжди намагаюся запам'ятати тип моїх суфіксів! Чи можете ви сказати, до якого типу належать мої суфікси?

Зліва-найбільше що?

Дуже багато алгоритмів сортування суфіксів (SAIS, KA, мій власний програмний засіб) групують суфікси у різні типи, щоб сортувати їх. Існує два основні типи: суфікси типу S та L. Суфікси типу S - це суфікси, які лексикографічно менше ( S maller), ніж наступний суфікс та L-тип, якщо він лексикографічно більший ( L arger). Самий лівої S-тип ( LMS-тип ) є тільки що: S-типу суфікса , який передує в L-типі суфіксом.

Особливістю цих суфіксів типу LMS є те, що колись ми їх сортуємо, ми можемо сортувати всі інші суфікси за лінійним часом! Хіба це не дивовижно?

Змагання

З огляду на те, що рядок припускається, він закінчується спеціальним символом, меншим за будь-який інший символ у цій рядку (наприклад, меншим, ніж навіть нульовий байт). Виведіть тип корроспондірующего символу для кожного суфікса.

Ви можете вільно вибирати, який знак використовувати для того чи іншого типу, але я вважаю L, S and *за краще L-, S- and LMS-type, поки вони всі друкуються ( 0x20 - 0x7E).

Приклад

З огляду на рядок mmiissiissiippiвиводу (при використанні L, S and *):

 LL*SLL*SLL*SLLL

Наприклад, перший Lпов'язаний з тим, що mmiissiissiippi$лексикографічно більше miissiissiippi$( $являє собою доданий мінімальний символ):

L - mmiissiissiippi$ > miissiissiippi$
L - miissiissiippi$  > iissiissiippi$
* - iissiissiippi$   < issiissiippi     and preceeded by L
S - issiissiippi$    < ssiissiippi$
L - ssiissiippi$     > siissiippi$
L - siissiippi$      > iissiippi$
* - iissiippi$       < issiippi$        and preceeded by L
S - issiippi$        < ssiippi$
L - ssiippi$         > siippi$
L - siippi$          > iippi$
* - iippi$           < ippi$            and preceeded by L
S - ippi$            < ppi$
L - ppi$             > pi$
L - pi$              > i$
L - i$               > $

Ще кілька прикладів:

"hello world" -> "L*SSL*L*LLL"
"Hello World" -> "SSSSL*SSLLL"
"53Ab§%5qS"   -> "L*SSL*SLL"

Мета

Я не тут, щоб дратувати Пітера Кордеса (я так збираюсь робити це колись по потоку); Я просто дуже ледачий, тому це звичайно ! Виграє найкоротша відповідь у байтах.


Редагувати: Порядок символів задається їх байтним значенням. Це означає порівняння повинно бути , як C - х strcmp.

Edit2: Як зазначено у коментарях, вихід повинен бути одним символом для кожного вхідного символу. Хоча я припускав, що це буде розумітися як "повернути рядок", здається, щонайменше 1 відповідь повертає список одиночних символів. Щоб не визнати недійсними відповіді, я дозволю вам повернути список одиничних символів (або цілих чисел, які при друкуванні дають лише 1 знак).


Поради щодо лінійного часу:

  1. Це може бути виконано у двох паралельних ітераціях вперед або в одній назад ітерації.
  2. Стан кожного суфікса залежить лише від перших 2 знаків та типу другого.
  3. Скануючи вхід у зворотному напрямку, ви можете визначити L або S так: $t=$c<=>$d?:$t(PHP 7), де $cпоточний знак $dпопереднього та $tпопереднього типу.
  4. Дивіться мою відповідь PHP . Завтра я вручу нагороду.

Це моє перше питання :) Пісочниця отримала дві версії та жодних коментарів, тому я думаю, що вона готова до публікації. Не соромтеся робити пропозиції!
Крістоф

Які символи можуть відображатися у вводі?
Мартін Ендер

@MartinEnder всі символи, що підтримують ваш рядок, наприклад, навіть нульовий байт для c++рядків стилю. Подумайте про це як двійкові дані.
Крістоф

Що *означає?
Leaky Nun

@LeakyNun *означає, що відповідний суфікс має тип left most s-type. A S-type suffix that is preceeded by a L-type suffix..
Крістоф

Відповіді:


7

Haskell , 64 53 48 42 байт

(0!)
k!(x:y)|x:y>y=1:2!y|2>1=k:0!y
_![]=[]

Спробуйте в Інтернеті!

Unololfed, з Charзамість Int:

suffixes :: String -> String
suffixes = go 'S'
 where
   go :: Char -> String -> String
   go _ "" = ""
   go lorstar s | s > tail s = 'L' : go '*' (tail s)
                | otherwise  = lorstar : go 'S' (tail s)

Анонімні функції дозволені, тому їх z=можна видалити.
Ørjan Johansen

Я просто не можу читати Haskell. Ви б не хотіли дати коротке пояснення?
Крістоф

1
@Christoph: goфункція бере два аргументи. Перший - персонаж, який представляє те, що слід використовувати для опису Sситуації. Друга - це струна. Він проходить через цей рядок рекурсивно, видаляючи перший символ на кожному кроці (саме це і tailробиться). Хитрість полягає в тому, що перший аргумент встановлюється, *коли попередній результат був а Lчи Sіншим чином. Таким чином, у випадку, коли слід використовувати *або або S, цей перший аргумент можна використовувати безпосередньо. Сподіваюся, що це має сенс.
bartavelle

Це дуже приємна ідея! Я сподіваюся побачити більш розумні ідеї :)
Крістоф

@ ØrjanJohansen, як я повинен підготувати результат у TIO?
bartavelle

6

Желе ,  25 23 21 20  19 байт

Ṛ;\UỤỤIṠµI2n×ịØDṚ;0

Повна програма, яка друкує список символів, використовуючи:

L: 0
S: 8
*: 9

(Як посилання, він повертає список, де всі елементи є символами, крім останнього, який дорівнює нулю.)

Спробуйте в Інтернеті! або переглянути тестовий набір (з перетворенням наLS*).

Як?

Ṛ;\UỤỤIṠµI2n×ịØDṚ;0 - Link: list of characters, s  e.g. "cast"
Ṛ                   - reverse                           "tsac"
  \                 - cumulative reduce by:
 ;                  -   concatenation                   ["t","ts","tsa","tsac"]
   U                - upend (reverse each)              ["t","st","ast","cast"] (suffixes)
    Ụ               - sort indexes by value             [3,4,2,1] (lexicographical order)
     Ụ              - sort indexes by value             [4,3,1,2] (order of that)
      I             - incremental differences           [-1,-2,1] (change)
       Ṡ            - sign                              [-1,-1,1] (comparisons)
        µ           - monadic chain separation, call that x
         I          - incremental differences           [0,2] (only (-1,1) produce 2s)
          2         - literal 2                         2
           n        - not equal?                        [1,0] (indexes of * will be 0)
            ×       - multiply by x (vectorises)        [-1,0,1] (make indexes of *s 0)
              ØD    - decimal yield                     "0123456789"
             ị      - index into (1-indexed & modular)  ['8','9','0']
                Ṛ   - reverse                           ['0','9','8']
                 ;0 - concatenate a zero                ['0','9','8',0]
                    - implicit print                     0980
                    -                              i.e. "L*SL"

Ви не хотіли б додати для мене невелике пояснення?
Крістоф

2
Я зроблю звичайно - я спочатку думаю про можливі гольфи ...
Джонатан Аллан


@LeakyNun Як ти це вчинив ?! Ви використовуєте помилку там , я думаю , що +на рядках , здається, vectorise але основні результати не є на самому ділі Jelly ітеріруемимі але рядки (наприклад , спробувати (!) +@/L€Або +@/L€€чи ...)
Джонатан Allan

@JonathanAllan так, +створює фактичний рядок. Це недокументована функція або те, що ви називаєте помилкою.
Leaky Nun

3

Python 3, 92 87 74 69 65 байт

s=input()
c=1
while s:d=s<s[1:];print(d+(c<d),end='');s=s[1:];c=d

Використовує 0для L, 1для Sі 2для *. Оберніть рядок введення цитатами; Я вважаю, що це дозволено конвенцією.

Спробуйте в Інтернеті!

Приклад використання:

mmiissiissiippi
002100210021000

врятувало 5 байт завдяки Leaky Nun, 4 байти завдяки ovs




3

JavaScript (ES6), 51 45 байт

f=(c,d)=>c&&(d<(d=c<(c=c.slice(1))))+d+f(c,d)

Збережено 6 байт завдяки @Neil.

Рекурсивне рішення вправи.

f=(c,d)=>c&&(d<(d=c<(c=c.slice(1))))+d+f(c,d)

console.log(f('mmiissiissiippi')); //LL*SLL*SLL*SLLL   002100210021000
console.log(f('hello world'));     //L*SSL*L*LLL       02110202000
console.log(f('Hello World'));     //SSSSL*SSLLL       11110211000
console.log(f('53Ab§%5qS'));       //L*SSL*SLL         021102100


Збережіть 6 байт:f=(c,d)=>c&&(d<(d=c<(c=c.slice(1))))+d+f(c,d)
Ніл

Дякую, @Neil, я знав, що десь там повинна бути оптимізація.
Рік Хічкок

2

JavaScript (ES6), 52 байти

f=
s=>s.replace(/./g,_=>(c<(c=s<(s=s.slice(1))))+c,c=1)
<input oninput=o.textContent=f(this.value)><pre id=o>

Порт відповіді @ L3viathan


1
@ RickHitchcock На жаль, мені якось вдалося перейти c=1як c=0
Ніл


1

Haskell , 77 75 байт, лінійний час

f(a:b:c)|let g"L"|a<b="SL";g"S"|a>b="L*";g d=d++d;d:e=f$b:c=g[d]++e
f _="L"

Спробуйте в Інтернеті!

Як це працює

Для цього використовується рекурсія, знімаючи одночасно один символ із початку рядка. (Тип рядка Haskell - це список зв’язків, що пов'язані між собою, тому кожен з цих кроків є постійним часом.)

  • Для рядка abc, де a і b є єдиними символами, а c - будь-який (можливо, порожній) рядок,
    • f ( abc ) = SL e , якщо f ( bc ) = L e і a < b ;
    • f ( abc ) = L * e , якщо f ( bc ) = S e і a > b ;
    • f ( abc ) = LL e , якщо f ( bc ) = L e і ab ;
    • f ( abc ) = SS e , якщо f ( bc ) = S e і ab .
  • Для однозначного рядка a , f ( a ) = L.

1
Чи можете ви надати пояснення?
Р. Кап

Будь ласка, надайте опис, щоб я міг підтвердити, що це працює в лінійний час.
Крістоф

@Christoph Додано
Андерс Касеорг

@AndersKaseorg дякую за додавання! На жаль, це здається досить багатослівним порівняно з іншою відповіддю Хаскелла. Чи можна це додатково гольфувати, не використовуючи S, L and *?
Крістоф

1
@Christoph Щоб бути зрозумілим, [1,1,2,0,1,1,2,0,1,1,2,0,1,1,1]це список одноцифрових чисел, а не список однозначних символів. У моєму випадку я думаю, що виведення списку чисел не врятує мене жодних байтів.
Андерс Касеорг

1

Python 2 , 65 55 байт

Рекурсивна версія, заснована на відповіді L3viathan , використовуючи 012як LS*:

def g(s,d=2):c=s<s[1:];return s and`c+(d<c)`+g(s[1:],c)

Спробуйте в Інтернеті!

Python 3 , 65 59 байт

Рекурсивне рішення з використанням L, Sі *:

f=lambda s:s and('LS'[s<s[1:]]+f(s[1:])).replace('LS','L*')

Працює через рядок з фронту, і замінює всі екземпляри LSзL*

Спробуйте в Інтернеті!


1
blah if s else''s and blahзберігає шість байтів. У Python 2 str(blah)`blah`зберігає ще три байти на другому рішенні.
Anders Kaseorg

1

PHP, 82 байт, лінійний час

for($a=$argn;a&$c=$a[$i-=1];$d=$c)$a[$i]=2+$t=$d<=>$c?:$t;echo strtr($a,[13=>12]);

Проходить над входом справа наліво та замінює кожну таблицю типом.

$t=$d<=>$c?:$t

Обчислює тип, заданий поточним та попереднім знаком (-1 або 1). Якщо рівне, тип не змінюється.


+1 за ідею зstrtr
Йорг Гюльсерманн

1

PHP , 70 байт

L = 1, S = 0, * = 2

Для останньої тестової шкали потрібна мультибайтова підтримка, а замість цього - §+3 байтиmb_substrsubstr

for(;$s=&$argn;$s=$u)$r.=$l=($l&1)+(1&$l^($s>$u=substr($s,1)));echo$r;

Спробуйте в Інтернеті!

PHP , 71 байт

L = 1, S = 0, * = 2

for(;$s=&$argn;$s=$u)$r.=+($s>$u=substr($s,1));echo strtr($r,[10=>12]);

Спробуйте в Інтернеті!

PHP , 74 байти

for(;$s=&$argn;$s=$u)$r.=SL[$s>$u=substr($s,1)];echo strtr($r,[LS=>"L*"]);

Спробуйте в Інтернеті!


$s=&$argnдосить розумний! Я майже впевнений, що є краща відповідь;) Сподіваюся, хтось придумає це :)
Крістоф

@Christoph У мене таке відчуття, що я щось сумую. Я намагаюся зберігати останній LS * у варіале, але він довший
Jörg Hülsermann

@Christoph означає, що ти так любиш? Я не дуже бачив, чому останній тест неправдивий Спробуйте його в Інтернеті!
Йорг Гюльсерманн

@Christoph Добре Я бачив, чому це не працює для останнього тестового шаблона, який я повинен використовувати mb_substrзамість того, substrякщо введення не в простому діапазоні ascii. Чи потрібно підтримувати останній тестовий зразок?
Йорг Гюльсерманн

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