Limite de tempo por teste: 5 segundos
Limite de memória por teste: 512 megabytesVocê recebe uma sequência
s
de comprimenton
(n
≤ 5000). Você pode selecionar qualquer prefixo adequado dessa sequência que também seja seu sufixo e remover o prefixo selecionado ou o sufixo correspondente. Em seguida, você pode aplicar uma operação análoga a uma sequência resultante e assim por diante. Qual é o comprimento mínimo da sequência final que pode ser alcançado após a aplicação da sequência ideal de tais operações?Entrada
A primeira linha de cada teste contém uma sequências
que consiste em pequenas letras em inglês.Saída
Produz um único inteiro - o comprimento mínimo da sequência final, que pode ser alcançado após a aplicação da sequência ideal de tais operações.Exemplos
+-------+--------+----------------------------------+ | 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 | +-------+--------+----------------------------------+
Aqui está o que eu consegui fazer até agora:
Calcular a função de prefixo para todas as substrings de uma determinada string em O (n ^ 2)
Verifique o resultado da execução de todas as combinações possíveis de operações em O (n ^ 3)
Minha solução passa em todos os testes em n
≤ 2000, mas excede o limite de tempo em 2000 < n
≤ 5000. Aqui está o código:
#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: Existe alguma solução mais eficiente?
fonte
Respostas:
Aqui está uma maneira de obter o fator de log. Sejamos
dp[i][j]
verdade se pudermos alcançar a substrings[i..j]
. Então:Agora itere de fora para:
Podemos precompute o jogo mais longo para cada par começando
(i, j)
emO(n^2)
com a recorrência,Isso nos permitiria verificar uma correspondência de substring iniciada em índices
i
ej
emO(1)
. (Precisamos das direções direita e esquerda.)Como obter o fator de log
Podemos pensar em uma maneira de criar uma estrutura de dados que nos permita determinar se
no
O(log n)
, considerando que já vimos esses valores.Aqui está o código C ++ com árvores de segmento (para consultas à direita e à esquerda, portanto
O(n^2 * log n)
) que inclui o gerador de testes do Bananon. Para 5000 caracteres "a", era executado em 3,54s e 420 MB ( https://ideone.com/EIrhnR ). Para reduzir a memória, uma das árvores de segmento é implementada em uma única linha (ainda preciso investigar o mesmo com as consultas do lado esquerdo para reduzir ainda mais a memória).E aqui está o código JavaScript (sem a melhoria do fator de log) para mostrar que a recorrência funciona. (Para obter o fator de log, substituímos os
k
loops internos por uma única consulta de intervalo.)fonte
i
da linha 64, a linha 99 é um pouco difícil de entender - isso é intencional? As declarações de loops em 98 e 99 parecem deixari
noMAX_N
restante do escopo de loop da linha 98? (Versão C ++)i
era apenas para o escopo desse loop de quatro linhas, mas poderia parecer confuso. Obrigado por apontar - eu mudei, embora a alteração não afete a execução do código.