Este problema envolve determinar se uma terceira string é formada pela intercalação de caracteres de duas outras strings, mantendo a ordem original dos caracteres dentro de cada uma das duas primeiras strings. Por exemplo, "cat" e "tree" podem formar "cattree", mas não "tretac".
Uma abordagem inicial para resolver este problema é a recursão. Podemos usar três ponteiros para rastrear a posição atual em cada uma das três strings, começando em 0. A função recursiva verifica se o caractere atual na terceira string corresponde ao caractere atual na primeira ou na segunda string. Se houver uma correspondência, avançamos o ponteiro correspondente e o ponteiro da terceira string e continuamos recursivamente.
#include <iostream>
#include <cstring>
char str1[201], str2[201], combinedStr[401];
int len1, len2, lenCombined;
bool canForm(int idx1, int idx2, int idxCombined) {
// Caso base: se todos os caracteres da string combinada foram verificados
if (idxCombined == lenCombined) {
return true;
}
bool possible = false;
// Tenta combinar com o caractere de str1
if (idx1 < len1 && str1[idx1] == combinedStr[idxCombined]) {
possible = possible || canForm(idx1 + 1, idx2, idxCombined + 1);
}
// Tenta combinar com o caractere de str2
if (idx2 < len2 && str2[idx2] == combinedStr[idxCombined]) {
possible = possible || canForm(idx1, idx2 + 1, idxCombined + 1);
}
return possible;
}
int main() {
int numTestCases;
std::cin >> numTestCases;
for (int i = 1; i <= numTestCases; ++i) {
std::cin >> str1 >> str2 >> combinedStr;
len1 = strlen(str1);
len2 = strlen(str2);
lenCombined = strlen(combinedStr);
// Verifica se o comprimento total das duas primeiras strings é igual ao da terceira
if (len1 + len2 != lenCombined) {
std::cout << "Data set " << i << ": no" << std::endl;
continue;
}
if (canForm(0, 0, 0)) {
std::cout << "Data set " << i << ": yes" << std::endl;
} else {
std::cout << "Data set " << i << ": no" << std::endl;
}
}
return 0;
}
A abordagem recursiva, no entanto, pode levar a um Time Limit Exceeded (TLE) devido à computação de subproblemas repetidos. Para otimizar, podemos usar programação dinâmica. Identificamos a necessidade de:
- Definir uma relação de recorrência clara.
- Confirmar a existência de subproblemas sobrepostos.
- Resolver o problema de forma bottom-up.
Os subproblemas sobrepostos não são imediatamente óbvios, surgindo apenas quando os caracteres de str1 e str2 coincidem em certas posições. Por exemplo, em "cat" e "tree", não há sobreposição significativa. A relação de recorrência pode ser definida como dp\[i\]\[j\], que indica se os primeiros i caracteres de str1 e os primeiros j caracteres de str2 podem formar os primeiros i + j caratceres de combinedStr.
dp\[i\]\[j\] é determinado pela verdade de uma das seguintes condições (ou uma combinação delas):
dp\[i-1\]\[j\]e oi-ésimo caractere destr1corrresponde ao(i+j)-ésimo caractere decombinedStr.dp\[i\]\[j-1\]e oj-ésimo caractere destr2corresponde ao(i+j)-ésimo caractere decombinedStr.
A implementação bottom-up utiliza uma tabela dp onde dp\[i\]\[j\] representa a possibilidade de formar combinedStr até o índice i+j-1 usando os primeiros i caracteres de str1 e os primeiros j caracteres de str2.
#include <iostream>
#include <vector>
#include <string>
#include <cstring>
char str1[201], str2[201], combinedStr[401];
bool dp[201][201];
int len1, len2, lenCombined;
int main() {
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
int numTestCases;
std::cin >> numTestCases;
for (int k = 1; k <= numTestCases; ++k) {
std::cin >> str1 >> str2 >> combinedStr;
len1 = strlen(str1);
len2 = strlen(str2);
lenCombined = strlen(combinedStr);
if (len1 + len2 != lenCombined) {
std::cout << "Data set " << k << ": no" << std::endl;
continue;
}
// Inicializa a tabela DP
memset(dp, false, sizeof(dp));
// Caso base: strings vazias formam uma string combinada vazia
dp[0][0] = true;
// Preenche a primeira coluna (considerando apenas str1)
for (int i = 1; i <= len1; ++i) {
if (dp[i - 1][0] && str1[i - 1] == combinedStr[i - 1]) {
dp[i][0] = true;
}
}
// Preenche a primeira linha (considerando apenas str2)
for (int j = 1; j <= len2; ++j) {
if (dp[0][j - 1] && str2[j - 1] == combinedStr[j - 1]) {
dp[0][j] = true;
}
}
// Preenche o restante da tabela DP
for (int i = 1; i <= len1; ++i) {
for (int j = 1; j <= len2; ++j) {
// Verifica se o caractere atual de combinedStr pode vir de str1
bool fromStr1 = dp[i - 1][j] && (str1[i - 1] == combinedStr[i + j - 1]);
// Verifica se o caractere atual de combinedStr pode vir de str2
bool fromStr2 = dp[i][j - 1] && (str2[j - 1] == combinedStr[i + j - 1]);
dp[i][j] = fromStr1 || fromStr2;
}
}
if (dp[len1][len2]) {
std::cout << "Data set " << k << ": yes" << std::endl;
} else {
std::cout << "Data set " << k << ": no" << std::endl;
}
}
return 0;
}
É importante notar que a condição dp\[i-1\]\[j-1\] mencionada na análise original não é estritamente necessária se considerarmos as transições a partir de dp\[i-1\]\[j\] e dp\[i\]\[j-1\] separadamente, pois elas já cobrem todos os casos.