Limit czasu na test: 5 sekund
Limit pamięci na test: 512 megabajtówOtrzymujesz ciąg
s
długościn
(n
≤ 5000). Możesz wybrać dowolny właściwy prefiks tego łańcucha, który jest również jego sufiksem, i usunąć albo wybrany prefiks, albo odpowiedni sufiks. Następnie możesz zastosować analogiczną operację do wynikowego ciągu i tak dalej. Jaka jest minimalna długość końcowego ciągu, którą można osiągnąć po zastosowaniu optymalnej sekwencji takich operacji?Wejście
Pierwszy wiersz każdego testu zawiera ciąg znakóws
składający się z małych angielskich liter.Wyjście
Wypisuje jedną liczbę całkowitą - minimalną długość końcowego ciągu, którą można osiągnąć po zastosowaniu optymalnej sekwencji takich operacji.Przykłady
+-------+--------+----------------------------------+ | Input | Output | Explanation | +-------+--------+----------------------------------+ | caaca | 2 | caaca → ca|aca → aca → ac|a → ac | +-------+--------+----------------------------------+ | aabaa | 2 | aaba|a → a|aba → ab|a → ab | +-------+--------+----------------------------------+ | abc | 3 | No operations are possible | +-------+--------+----------------------------------+
Oto, co udało mi się dotychczas zrobić:
Oblicz funkcję prefiksu dla wszystkich podciągów danego ciągu w O (n ^ 2)
Sprawdź wynik wykonania wszystkich możliwych kombinacji operacji w O (n ^ 3)
Moje rozwiązanie przechodzi wszystkie testy przy n
≤ 2000, ale przekracza limit czasu, gdy 2000 < n
≤ 5000. Oto jego kod:
#include <iostream>
#include <string>
using namespace std;
const int MAX_N = 5000;
int result; // 1 less than actual
// [x][y] corresponds to substring that starts at position `x` and ends at position `x + y` =>
// => corresponding substring length is `y + 1`
int lps[MAX_N][MAX_N]; // prefix function for the substring s[x..x+y]
bool checked[MAX_N][MAX_N]; // whether substring s[x..x+y] is processed by check function
// length is 1 less than actual
void check(int start, int length) {
checked[start][length] = true;
if (length < result) {
if (length == 0) {
cout << 1; // actual length = length + 1 = 0 + 1 = 1
exit(0); // 1 is the minimum possible result
}
result = length;
}
// iteration over all proper prefixes that are also suffixes
// i - current prefix length
for (int i = lps[start][length]; i != 0; i = lps[start][i - 1]) {
int newLength = length - i;
int newStart = start + i;
if (!checked[start][newLength])
check(start, newLength);
if (!checked[newStart][newLength])
check(newStart, newLength);
}
}
int main()
{
string str;
cin >> str;
int n = str.length();
// lps calculation runs in O(n^2)
for (int l = 0; l < n; l++) {
int subLength = n - l;
lps[l][0] = 0;
checked[l][0] = false;
for (int i = 1; i < subLength; ++i) {
int j = lps[l][i - 1];
while (j > 0 && str[i + l] != str[j + l])
j = lps[l][j - 1];
if (str[i + l] == str[j + l]) j++;
lps[l][i] = j;
checked[l][i] = false;
}
}
result = n - 1;
// checking all possible operations combinations in O(n^3)
check(0, n - 1);
cout << result + 1;
}
P: Czy jest jakieś bardziej wydajne rozwiązanie?
źródło
Odpowiedzi:
Oto jeden ze sposobów uzyskania współczynnika dziennika. Bądźmy
dp[i][j]
prawdą, jeśli uda nam się dotrzeć do podłańcuchas[i..j]
. Następnie:Teraz iteruj z zewnątrz w:
Możemy wstępnego wyliczenia najdłuższy mecz dla każdej pary rozpoczynającego
(i, j)
sięO(n^2)
z nawrotem,Pozwoliłoby nam to sprawdzić dopasowanie podciągu, które zaczyna się od indeksów
i
ij
odO(1)
. (Potrzebujemy zarówno prawej, jak i lewej strony).Jak uzyskać współczynnik dziennika
Możemy wymyślić sposób wymyślenia struktury danych, która pozwoliłaby nam ustalić, czy
w
O(log n)
, biorąc pod uwagę, że widzieliśmy już te wartości.Oto kod C ++ z drzewami segmentów (dla zapytań po prawej i lewej stronie
O(n^2 * log n)
), który zawiera generator testowy Bananona. W przypadku 5000 znaków „a” działał on w 3,54 s, 420 MB ( https://ideone.com/EIrhnR ). Aby zmniejszyć pamięć, jedno z drzew segmentów jest zaimplementowane w jednym rzędzie (nadal muszę to zbadać, robiąc to samo z zapytaniami po lewej stronie, aby jeszcze bardziej zmniejszyć pamięć).A oto kod JavaScript (bez poprawy współczynnika logarytmicznego), aby pokazać, że rekurencja działa. (Aby uzyskać współczynnik log, zastępujemy wewnętrzne
k
pętle zapytaniem o jednym zakresie).źródło
i
od linii 64 na początku linii 99 trudno mi się rozejrzeć - czy to celowe? Oświadczenia w pętle 98 i 99 wydaje się, aby pozostawići
coMAX_N
na pozostałej części zakresu pętli linii 98? (Wersja C ++)i
był tylko dla zakresu tej czteroliniowej pętli, ale mógł wyglądać na zagmatwany. Dziękujemy za wskazanie go - zmieniłem go, chociaż zmiana nie wpływa na wykonanie kodu.