\documentclass[10pt]{article} \usepackage[utf8]{inputenc} \usepackage{amsmath,amsthm,amssymb} \usepackage{fullpage} \usepackage{url} \pagenumbering{gobble} \usepackage{hyperref} \usepackage{graphicx} \input{statement/preamble.tex} \title{ Tutorial: Maior Subsequência Comum} \author{} \date{} \begin{document} \maketitle \section{Solução do Problema} O problema da maior subsequência comum (\textit{Longest Common Subsequence}, LCS) pode ser resolvido por meio de \textit{programação dinâmica}. A ideia central é decompor o problema em subproblemas menores, de forma que a solução ótima final seja construída a partir das soluções ótimas desses subproblemas. \subsection{Definição do Subproblema} Sejam duas strings \( s_1 \) e \( s_2 \), de tamanhos \( n \) e \( m \), respectivamente. Definimos: \[ dp[i][j] = \text{o comprimento da maior subsequência comum entre } s_1[1..i] \text{ e } s_2[1..j]. \] Isto é, \( dp[i][j] \) representa a resposta do problema considerando apenas os primeiros \( i \) caracteres de \( s_1 \) e os primeiros \( j \) caracteres de \( s_2 \). \subsection{Função de Transição} Ao analisar um par de posições \( (i, j) \), temos duas possibilidades principais: \begin{itemize} \item Se os caracteres atuais são iguais, isto é, \( s_1[i] = s_2[j] \), então podemos estender uma subsequência comum encontrada anteriormente: \[ dp[i][j] = dp[i-1][j-1] + 1. \] \item Caso contrário, não é possível usar ambos os caracteres ao mesmo tempo, então devemos escolher o melhor resultado entre ignorar um dos dois: \[ dp[i][j] = \max(dp[i-1][j], \, dp[i][j-1]). \] \end{itemize} Assim, a função de transição pode ser resumida como: \[ dp[i][j] = \begin{cases} dp[i-1][j-1] + 1, & \text{se } s_1[i] = s_2[j] \\ \max(dp[i-1][j], \, dp[i][j-1]), & \text{caso contrário} \end{cases} \] \subsection{Casos Base} Os casos base seguem a lógica natural do problema: \[ dp[0][j] = 0 \quad \forall j \ge 0 \] \[ dp[i][0] = 0 \quad \forall i \ge 0 \] Isto representa que, se uma das strings tiver tamanho zero, nenhuma subsequência comum pode ser formada. \subsection{Recuperação da Subsequência Comum} Após o preenchimento da matriz \( dp \), o valor \( dp[n][m] \) fornece o comprimento da maior subsequência comum entre \( s_1 \) e \( s_2 \). No entanto, para obter também a subsequência em si, é necessário realizar um processo de \textit{backtracking} sobre a matriz. A ideia consiste em iniciar a partir da posição \( (n, m) \) da matriz e caminhar em direção à origem. Em cada passo: \begin{itemize} \item Se \( s_1[i] = s_2[j] \), então este caractere faz parte da LCS. Nesse caso, retrocedemos para \( (i-1, j-1) \). \item Caso contrário, avançamos para o vizinho que contém o maior valor entre \( dp[i-1][j] \) e \( dp[i][j-1] \), pois esse vizinho indica o caminho que manteve o comprimento máximo da subsequência. \end{itemize} Como a subsequência é reconstruída de trás para frente, os caracteres encontrados devem ser adicionados no início da string resultante. O algoritmo para recuperar uma subsequência comum de comprimento máximo é o seguinte: \begin{verbatim} int i = n; // ponteiro para s1 int j = m; // ponteiro para s2 string lcs = ""; // subsequência comum sendo reconstruída // Enquanto ainda houver caracteres a considerar while (i > 0 && j > 0) { // Caso 1: os caracteres são iguais → fazem parte da LCS if (s1[i - 1] == s2[j - 1]) { lcs = s1[i - 1] + lcs; // adiciona no início da string i--; j--; } // Caso 2: se o valor de cima é maior, movemos para cima else if (dp[i - 1][j] > dp[i][j - 1]) { i--; } // Caso 3: caso contrário, movemos para a esquerda else { j--; } } \end{verbatim} Ao final desse processo, a string \texttt{lcs} conterá uma maior subsequência comum entre as duas strings.\end{document}