Limite de temps par test: 5 secondes
Limite de mémoire par test: 512 mégaoctetsOn vous donne une chaîne
s
de longueurn
(n
≤ 5000). Vous pouvez sélectionner n'importe quel préfixe approprié de cette chaîne qui est également son suffixe et supprimer le préfixe sélectionné ou le suffixe correspondant. Vous pouvez ensuite appliquer une opération analogue à une chaîne résultante, etc. Quelle est la longueur minimale de la chaîne finale, qui peut être atteinte après l'application de la séquence optimale de ces opérations?Entrée
La première ligne de chaque test contient une chaînes
composée de petites lettres anglaises.Sortie
Affiche un seul entier - la longueur minimale de la chaîne finale, qui peut être atteinte après l'application de la séquence optimale de ces opérations.Exemples
+-------+--------+----------------------------------+ | 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 | +-------+--------+----------------------------------+
Voici ce que j'ai réussi à faire jusqu'à présent:
Calculer la fonction de préfixe pour toutes les sous-chaînes d'une chaîne donnée dans O (n ^ 2)
Vérifier le résultat de l'exécution de toutes les combinaisons possibles d'opérations dans O (n ^ 3)
Ma solution passe tous les tests à n
≤ 2000 mais dépasse la limite de temps lorsque 2000 < n
≤ 5000. Voici son 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;
}
Q: Existe - t-il une solution plus efficace?
la source
Réponses:
Voici une façon d'obtenir le facteur de journalisation. Soyons
dp[i][j]
vrais si nous pouvons atteindre la sous-chaînes[i..j]
. Alors:Maintenant, itérez de l'extérieur dans:
On peut précalculer le plus long match pour chaque paire à partir
(i, j)
deO(n^2)
la récurrence,Cela nous permettrait de vérifier une correspondance de sous-chaîne qui commence aux index
i
etj
dansO(1)
. (Nous avons besoin de directions à droite et à gauche.)Comment obtenir le facteur logarithmique
Nous pouvons penser à un moyen de créer une structure de données qui nous permettrait de déterminer si
en
O(log n)
considérant que nous avons déjà vu ces valeurs.Voici du code C ++ avec des arborescences de segments (pour les requêtes droite et gauche, donc
O(n^2 * log n)
) qui inclut le générateur de test de Bananon. Pour 5000 caractères "a", il a fonctionné en 3,54 s, 420 Mo ( https://ideone.com/EIrhnR ). Pour réduire la mémoire, l'une des arborescences de segments est implémentée sur une seule ligne (j'ai encore besoin d'envisager de faire de même avec les requêtes de gauche pour réduire encore plus la mémoire.)Et voici du code JavaScript (sans l'amélioration du facteur de journalisation) pour montrer que la récurrence fonctionne. (Pour obtenir le facteur de log, nous remplaçons les
k
boucles internes par une requête à plage unique.)la source
i
de la ligne 64, le début de la ligne 99 est un peu difficile à comprendre - est-ce intentionnel? Les déclarations de boucles à 98 et 99 semblent laisseri
àMAX_N
pour le reste de la portée de la boucle de la ligne 98? (Version C ++)i
c'était uniquement pour la portée de cette boucle de quatre lignes, mais cela pouvait sembler déroutant. Merci de l'avoir signalé - je l'ai changé, bien que le changement n'affecte pas l'exécution du code.