tło
Incydent jest dość nietypowym językiem programowania, ponieważ jego lista tokenów nie jest z góry określona, ale raczej wywodzi się z danych wejściowych. Dlatego tokenizacja programu Incydent może być dość trudna, szczególnie jeśli chcesz to zrobić skutecznie. To zadanie polega na robieniu tego samemu.
Zadanie
Twój program otrzyma ciąg wejściowy. Oto algorytm wykorzystywany przez Incydent do tokenizacji:
- Zidentyfikuj wszystkie ciągi, które występują jako podciąg danych wejściowych na dokładnie trzy sposoby (tj. Są dokładnie trzy wystąpienia tego ciągu w danych wejściowych).
- Odrzucić każdy z tych łańcuchów, które są podłańcuchem innego takiego napisu (np dla wejścia
ababab
, jedyne ciąg byłobyab
, niea
bądźb
, boa
ib
to zarówno podrzędnymiab
). - Odrzuć wszystkie ciągi, które nakładają się na dane wejściowe. (Na przykład
aaaa
zawiera dokładnie trzy kopieaa
, ale kopie te nakładają się na drugi i trzeci znak, więc zostałyby odrzucone. Podobnie, wabababa
, są trzy kopieab
i trzy kopieba
, ale każda z drugiej do szóstej litery jest na nachodzenieab
iba
, tak jakab
iba
byłyby usunięte). - Wszelkie ciągi, które pozostaną w tym momencie, są tokenami używanymi przez program. Tokenizuj oryginalne dane wejściowe w sekwencji tych żetonów (ze względu na odrzucenie w poprzednim kroku będzie tylko jeden sposób, aby to zrobić). Wszelkie znaki na wejściu, które nie są częścią żadnego tokena, są traktowane jako komentarze i odrzucane.
Twój program musi pobrać ciąg jako dane wejściowe i zwrócić odpowiednią tokenizację ciągu (listę tokenów, z których każdy jest wyrażony jako ciąg) jako wynik. Ponadto należy to zrobić co najmniej umiarkowanie skutecznie; w szczególności program musi działać w czasie kwadratowym („O (n²)”) lub lepszym. (Nawiasem mówiąc, prawie na pewno można przejść szybciej niż kwadratowe, ale nie jest to najszybszy algorytm , więc możesz użyć najkrótszego algorytmu, jaki można znaleźć, który mieści się w granicach złożoności.)
Wyjaśnienia
- Chociaż programy incydentów mogą teoretycznie zawierać dowolny z 256 oktetów, dla celów tego wyzwania dopuszczalne jest, aby Twój program obsługiwał tylko dane wejściowe utworzone z drukowalnego ASCII (w tym spacji) oraz znak nowej linii i tabulator. (Wszystkie znane programy incydentów ograniczają się do tego podzbioru). Zauważ, że spacja / nowa linia / tab nie są wyjątkowe i mogą pojawiać się pośrodku tokenów; Incydent traktuje wszystkie 256 oktetów jako nieprzejrzyste.
- Definicja „czasu kwadratowego” brzmi „jeśli rozmiar wejścia zostanie podwojony, program będzie działał wolniej o nie więcej niż stałą plus współczynnik 4”, tj. Jeśli t ( x ) to maksymalny czas, jaki zajmuje Twój program przetworzyć dane wejściowe o rozmiarze x , wtedy musi być pewna stała k, taka, że t (2 x ) <4 t ( x ) + k dla wszystkich x . Pamiętaj, że porównywanie łańcuchów zajmuje czas proporcjonalny do długości łańcuchów.
- Twój program powinien teoretycznie być w stanie obsłużyć programy wejściowe dowolnej długości, jeśli jest uruchamiany w (prawdopodobnie hipotetycznym) wariancie języka, który ma nieograniczoną pamięć i używa nieograniczonych liczb całkowitych (jest OK, jeśli program nie osiągnie tego celu, gdy jest uruchomiony w praktyce z powodu liczby całkowite języka lub pamięć są w rzeczywistości skończone duże). Możesz założyć (w celu obliczenia złożoności), że liczby całkowite, które nie są większe niż długość danych wejściowych, mogą być porównywane w stałym czasie (chociaż pamiętaj, że jeśli użyjesz większych wartości, np. Ze względu na konwersję danych wejściowych na pojedynczej liczby całkowitej, porównanie zajmie dużo czasu proporcjonalnie do liczby cyfr, które mają).
- Możesz użyć dowolnego algorytmu, który mieści się w granicach złożoności, nawet jeśli nie wykonuje on tych samych kroków, co algorytm opublikowany powyżej, pod warunkiem, że daje takie same wyniki.
- Ta łamigłówka dotyczy tokenizacji danych wejściowych, a nie formatowania danych wyjściowych. Jeśli najbardziej naturalny sposób na wyświetlenie listy w twoim języku wymaga niejednoznacznego formatu (np. Rozdzielony znakiem nowej linii, gdy ciągi zawierają dosłowne znaki nowego wiersza lub bez ograniczników między ciągami), nie martw się faktem, że wynik jest niejednoznaczny ( tak długo, jak lista jest faktycznie konstruowana). Możesz chcieć stworzyć drugą wersję swojego zgłoszenia, która generuje jednoznaczne dane wyjściowe, aby pomóc w testowaniu, ale oryginalna wersja jest wersją, która liczy się do oceny.
Przypadek testowy
Dla następującego ciągu wejściowego:
aaabcbcbcdefdfefedghijghighjkllkklmmmmonono-nonppqpq-pqprsrsrstststuvuvu
twój program powinien wygenerować następującą listę wyników:
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
Warunek zwycięstwa
Jest to kod-golf , więc wygrywa najkrótszy prawidłowy (tj. Prawidłowe zachowanie wejścia / wyjścia i wystarczająco szybki do wykonania) program, mierzony w bajtach.
Odpowiedzi:
C (gcc), 324 bajty
Funkcja
f
pobiera łańcuch zakończony znakiem null i drukuje tokeny na standardowe wyjście. Wszystkie nowe wiersze można usunąć z kodu poniżej.Ta starsza 376-bajtowa wersja jest nieco łatwiejsza do odczytania; dotyczy to poniższe wyjaśnienie.
k(0)
generuje tabelęt
wzorówp
dla algorytmu Knuth – Morris – Pratt.K(c)
przetwarza następny znakc
ciągu wyszukiwania i aktualizacjim
, którego długość jest największym prefiksem,p
kończącym się na ostatnio przetwarzanym znaku.W pierwszej
for
pętli, dla każdego indeksua
w ciągu, liczymy, ile razy każda możliwa wartośćm
występuje podczas wyszukiwania w całym ciągu dla podłańcucha rozpoczynającego się oda
. Następnie szukamy największegol
takiego, żel
podciąg długości zaczyna sięa
dokładnie 3 razy. Jeśli jest wystarczająco krótki, aby w całości mógł go zawierać ciąg znaleziony dla poprzedniegoa
, ignorujemy go. Jeśli się nakłada, usuwamy poprzedni ciąg znaków zz
tablicy rejestrującej, które tokeny zostaną zachowane. W przeciwnym razie jego długość jest przechowywana wz
.Następnie ponownie używamy KMP, aby wyszukać ciąg znaków w celu znalezienia tokenów zarejestrowanych w
z
. Jeśli jeden z nich zostanie znaleziony w miejscu z wpisem 0z
, wiemy, że ten token został usunięty z powodu nakładania się. Jeśli token nie został usunięty, zostanie wydrukowany.źródło
O(n^2)
lub szybciej. I dlaczego jest!!
w!!z[x-m]
?*Z
jest długością następnego tokena, który musi stać się 0, jeśli którekolwiek z pozostałych wystąpień tokena ma wartość 0 przy indeksie w tablicy lub zachowuje tę samą wartość w przeciwnym razie (w takim przypadku!!z[x-m]
powinna wynosić 1.!!
jest.!!x
powinno być nadalx
, czy też wywołuje sztuczkę, której nie znam?!!x
sprawiax
logiczną reprezentującą jego „truthiness”. Więc!!1 == true
i!!0 == false
. Nie znam konkretnie C, ale tak zwykle bywaJavaScript,
878867842825775752717712704673664650641 bajtówDzięki @Kritixi Lithos za pomoc w
grze w golfa Dzięki @ User2428118 za grę w golfa z 14 bajtów
(Nie działa w IE7) (Newlines należy wpisać jako „
\n
” i tabulować jako „\t
” w ciągu wejściowym, wszelkie znaki Unicode należy wprowadzić jako\u####
)Wypróbuj online
Wyjaśnienie, jak to działa i niepoznany kod
Po pierwsze, program generuje tablice Knuth Morris Pratt dla każdego możliwego podciągu;
Następnie program wyszukuje maksymalne pasujące długości dla każdego indeksu w słowie z każdym podciągiem. (jest to czas O (n ^ 2))
Program wykorzystuje te dane, aby znaleźć najdłuższe podciągi, które pojawiają się trzy razy dla każdego początkowego znaku w ciągu.
Program wykorzystuje te dane, aby wyeliminować wszystkie podciągi, które nie pojawiają się dokładnie trzy razy i wszystkie podłańcuchy najdłuższych prawidłowych podłańcuchów.
Następnie program ustawia wszystkie nakładające się lub częściowe podciągi do usunięcia.
Dla każdej z wartości, które mają zostać usunięte, usuwane są również wszystkie równoważne podciągi.
Wreszcie program łączy ze sobą szereg podciągów i wysyła je.
źródło
while
iif
bloki, które mają tylko 1 Oświadczenie wewnątrz nich. Możesz usunąć nawiasy klamrowe{}
wokół tej instrukcji. Na przykładif(word.charAt(index+i)==word.charAt(index+j)){j++;}
może zostaćif(word.charAt(index+i)==word.charAt(index+j))j++;
&&
s do zamianyif
instrukcji, przesuwałem instrukcje w pętlach, aby skończyły się jedną instrukcją pod nimi, aby móc usunąć nawiasy klamrowe. Użyłem trójek, aby zastąpić niektóre instrukcje if. Przesuwałem rzeczy i skończyłem na 946 bajtach . Jeśli nie rozumiesz czegoś, co zrobiłem, możesz mnie zapytać :)