Zeitlimit pro Test: 5 Sekunden
Speicherlimit pro Test: 512 MegabyteSie erhalten eine Zeichenfolge mit
s
einer Längen
(n
≤ 5000). Sie können jedes richtige Präfix dieser Zeichenfolge auswählen, das auch das Suffix ist, und entweder das ausgewählte Präfix oder das entsprechende Suffix entfernen. Dann können Sie eine analoge Operation auf eine resultierende Zeichenfolge usw. anwenden. Was ist die Mindestlänge der endgültigen Zeichenfolge, die nach Anwendung der optimalen Reihenfolge solcher Operationen erreicht werden kann?Eingabe
Die erste Zeile jedes Tests enthält eine Zeichenfolges
, die aus kleinen englischen Buchstaben besteht.Ausgabe
Gibt eine einzelne Ganzzahl aus - die minimale Länge der endgültigen Zeichenfolge, die nach Anwendung der optimalen Reihenfolge solcher Operationen erreicht werden kann.Beispiele
+-------+--------+----------------------------------+ | 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 | +-------+--------+----------------------------------+
Folgendes habe ich bisher geschafft:
Berechnen Sie die Präfixfunktion für alle Teilzeichenfolgen einer bestimmten Zeichenfolge in O (n ^ 2).
Überprüfen Sie das Ergebnis der Ausführung aller möglichen Kombinationen von Operationen in O (n ^ 3).
Meine Lösung besteht alle Tests bei n
≤ 2000, überschreitet jedoch das Zeitlimit bei 2000 < n
≤ 5000. Hier ist der Code:
#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;
}
F: Gibt es eine effizientere Lösung?
quelle
Antworten:
Hier ist eine Möglichkeit, den Protokollfaktor zu ermitteln. Sei
dp[i][j]
wahr, wenn wir den Teilstring erreichen könnens[i..j]
. Dann:Jetzt von außen iterieren in:
Wir können die längste Übereinstimmung für jedes Ausgangspaar precompute
(i, j)
inO(n^2)
der Wiederholung,Dies würde es uns ermöglichen, nach einer Teilstring-Übereinstimmung zu suchen, die bei Indizes
i
undj
in beginntO(1)
. (Wir brauchen sowohl die rechte als auch die linke Richtung.)So erhalten Sie den Protokollfaktor
Wir können uns einen Weg ausdenken, um eine Datenstruktur zu entwickeln, mit der wir feststellen können, ob
in
O(log n)
Anbetracht dessen, dass wir diese Werte bereits gesehen haben.Hier ist C ++ - Code mit Segmentbäumen (für rechte und linke Abfragen
O(n^2 * log n)
), der den Testgenerator von Bananon enthält. Für 5000 "a" -Zeichen lief es in 3,54s, 420 MB ( https://ideone.com/EIrhnR ). Um den Speicher zu reduzieren, wird einer der Segmentbäume in einer einzelnen Zeile implementiert (ich muss noch untersuchen, ob ich dasselbe mit den Abfragen auf der linken Seite mache, um den Speicher noch weiter zu reduzieren.)Und hier ist JavaScript-Code (ohne Verbesserung des Protokollfaktors), um zu zeigen, dass die Wiederholung funktioniert. (Um den Protokollfaktor zu erhalten, ersetzen wir die inneren
k
Schleifen durch eine einzelne Bereichsabfrage.)quelle
i
Von Zeile 64 ab Zeile 99 beschattet ist ein bisschen schwer, meinen Kopf herumzukriegen - ist das beabsichtigt? Die Schleifen Erklärungen bei 98 & 99 erscheinen zu lasseni
umMAX_N
für den Rest des 98 Leitungsschleife Umfangs? (C ++ - Version)i
war nur für den Umfang dieser vierzeiligen Schleife, aber es könnte verwirrend aussehen. Vielen Dank für den Hinweis - ich habe es geändert, obwohl die Änderung die Codeausführung nicht beeinflusst.