Batas waktu per tes: 5 detik
Batas memori per tes: 512 megabyteAnda diberi untaian
s
panjangn
(n
≤ 5000). Anda dapat memilih awalan yang tepat dari string ini yang juga sufiksnya dan menghapus awalan yang dipilih atau sufiks yang sesuai. Kemudian Anda dapat menerapkan operasi analog ke string yang dihasilkan dan sebagainya. Berapa panjang minimum dari string terakhir, yang dapat dicapai setelah menerapkan urutan optimal dari operasi tersebut?Input
Baris pertama dari setiap tes berisi strings
yang terdiri dari huruf-huruf bahasa Inggris kecil.Keluaran
Keluaran bilangan bulat tunggal - panjang minimum string akhir, yang dapat dicapai setelah menerapkan urutan optimal dari operasi tersebut.Contohnya
+-------+--------+----------------------------------+ | 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 | +-------+--------+----------------------------------+
Inilah yang telah saya lakukan sejauh ini:
Hitung fungsi awalan untuk semua substring dari string yang diberikan dalam O (n ^ 2)
Periksa hasil dari melakukan semua kemungkinan kombinasi operasi di O (n ^ 3)
Solusi saya melewati semua tes pada n
≤ 2000 tetapi melebihi batas waktu ketika 2000 < n
≤ 5000. Berikut adalah kodenya:
#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;
}
T: Apakah ada solusi yang lebih efisien?
sumber
Jawaban:
Inilah salah satu cara untuk mendapatkan faktor log. Biarkan
dp[i][j]
menjadi kenyataan jika kita dapat mencapai substrings[i..j]
. Kemudian:Sekarang beralih dari luar ke dalam:
Kita bisa precompute pertandingan terpanjang untuk masing-masing pasangan mulai
(i, j)
diO(n^2)
dengan kekambuhan,Ini memungkinkan kami memeriksa kecocokan substring yang dimulai pada indeks
i
danj
masukO(1)
. (Kami membutuhkan arah kanan dan kiri.)Cara mendapatkan faktor log
Kita dapat memikirkan cara untuk membuat struktur data yang memungkinkan kita menentukan apakah
di
O(log n)
, mengingat kita sudah melihat nilai-nilai itu.Berikut kode C ++ dengan pohon segmen (untuk kueri kanan dan kiri, jadi
O(n^2 * log n)
) yang mencakup generator tes Bananon. Untuk 5000 "a" karakter, itu berjalan dalam 3,54s, 420 MB ( https://ideone.com/EIrhnR ). Untuk mengurangi memori, salah satu ruas pohon diimplementasikan pada satu baris (saya masih perlu menyelidiki melakukan hal yang sama dengan permintaan sisi kiri untuk mengurangi memori lebih jauh.)Dan inilah kode JavaScript (tanpa perbaikan faktor log) untuk menunjukkan bahwa perulangan berfungsi. (Untuk mendapatkan faktor log, kami mengganti
k
loop batin dengan kueri rentang tunggal.)sumber
i
dari garis 64 garis awal 99 agak sulit untuk membuat kepala saya pusing - apakah itu disengaja? Loop deklarasi pada 98 & 99 muncul untuk meninggalkani
diMAX_N
untuk sisa dari garis 98 lingkup lingkaran? (Versi C ++)i
itu hanya untuk lingkup loop empat baris itu, tetapi itu bisa terlihat membingungkan. Terima kasih telah menunjukkannya - saya mengubahnya, meskipun perubahan itu tidak mempengaruhi eksekusi kode.