Najdłuższy wspólny prefiks i najdłuższy wspólny podciąg to dwa istotne pojęcia w dziedzinie algorytmów i przetwarzania ciągów znaków.
Spis treści
- 1 Najdłuższy wspólny prefiks i najdłuższy wspólny podciąg – wprowadzenie
- 2 Najdłuższy wspólny prefiks
- 3 Drzewo Trie
- 4 Algorytm znajdujący najdłuższy wspólny prefix
- 5 Najdłuższy wspólny podciąg
- 6 Najdłuższy wspólny prefiks | Najdłuższy wspólny podciąg – podsumowanie
- 7 20+ BONUSOWYCH materiałów z programowania
Najdłuższy wspólny prefiks i najdłuższy wspólny podciąg – wprowadzenie
Z tego materiału dowiesz się:
- Czym jest najdłuższy wspólny prefix?
- Co to jest drzewo typu Trie?
- Jak zaimplementować algorytm znajdujący najdłuższy wspólny prefix?
- Czym jest najdłuższy wspólny podciąg?
Najdłuższy wspólny prefiks
Najdłuższy wspólny prefiks to najdłuższy początkowy fragment dwóch lub więcej ciągów znaków.
Na przykład, dla ciągów „program” i „projekt”, najdłuższy wspólny prefiks to „pro”.
Najdłuższy wspólny prefiks jest używany np. aby zidentyfikować wspólną część początkową w ciągach, co jest przydatne w zadaniach, takich jak porównywanie ciągów znaków, analiza słów kluczowych itp.
Podczas wyliczania najdłuższego wspólnego prefiksu możemy wykorzystać drzewo Trie (nie mylić z tree!).
Drzewo Trie
Drzewo Trie (zwane również drzewem prefixowym lub cyfrowym) jest strukturą danych specjalnie zaprojektowaną do przechowywania i przeszukiwania zestawów ciągów znaków.

Zaletą drzewa Trie jest to, że pozwala na wydajne znajdowanie wspólnego prefiksu ciągów znaków, ponieważ przechowuje je w formie drzewa, gdzie wspólny prefiks dla wielu ciągów jest wspólną ścieżką w drzewie.
Algorytmy oparte na drzewach Trie często mają lepszą wydajność niż podejścia oparte na porównywaniu znak po znaku.
class TrieNode {
Map<Character, TrieNode> children = new HashMap<>();
boolean isEndOfWord = false;
}
class TrieNodeMain {
public static String findLongestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
TrieNode root = new TrieNode();
for (String str : strs) {
TrieNode node = root;
for (char c : str.toCharArray()) {
node.children.putIfAbsent(c, new TrieNode());
node = node.children.get(c);
}
node.isEndOfWord = true;
}
StringBuilder prefix = new StringBuilder();
TrieNode node = root;
while (node.children.size() == 1 && !node.isEndOfWord) {
Map.Entry<Character, TrieNode> entry = node.children.entrySet().iterator().next();
prefix.append(entry.getKey());
node = entry.getValue();
}
return prefix.toString();
}
}
Powyższa metoda przyjmuje jako argument tablicę String. Po sprawdzeniu, czy tablica jest pusta inicjuje węzeł drzewa Trie. W kolejnym etapie iteruje po dostarczonych ciągach znaków, a następnie po znakach w bieżącym ciągu. Dalej w pętli dodawany jest nowy węzeł dla każdego znaku jeśli taki jeszcze nie istnieje. Tworzy obiekt StringBuilder do przechowywania wynikowego prefiksu. Na koniec w pętli while szuka najdłuższego prefixu.
➡ ZOBACZ 👉: StringBuilder: czy zawsze taki szybki? | String vs StringBuilder vs StringBuffer
Algorytm znajdujący najdłuższy wspólny prefix
Do znalezienia najdłuższego wspólnego prefixu możemy wykorzystać alternatywne rozwiązania. O to jedno z nich.
public static String findLongestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
String prefix = strs[0];
for (int i = 1; i < strs.length; i++) {
while (strs[i].indexOf(prefix) != 0) {
prefix = prefix.substring(0, prefix.length() - 1);
if (prefix.isEmpty()) {
return "";
}
}
}
return prefix;
}
Tutaj podobnie jak w poprzedniej implementacji na start dostarczamy tablicę z ciągami znaków.
Rozpoczynamy od pierwszego ciągu, a następnie stopniowo skracamy go, porównując z pozostałymi ciągami, aż znajdziemy wspólny początek.
Ewentualnie zwrócimy pusty ciąg, jeśli nie ma wspólnego fragmentu.
➡ ZOBACZ 👉: String – najważniejszy typ danych
Najdłuższy wspólny podciąg
Najdłuższy wspólny podciąg (ang. Longest Common Subsequence, LCS) to najdłuższy ciąg znaków, który występuje w tej samej kolejności w obu łańcuchach. Znaki te mogą być rozdzielone w każdym z łańcuchów innymi znakami.
[LCS] to nie tylko teoretyczny problem algorytmiczny. Rozumienie tego zagadnienia ma praktyczne zastosowania.Pozwala między innymi na porównywanie tekstów, znajdowanie wspólnych sekwencji w danych genetycznych czy śledzenie zmian w dokumentach.
public static String lcs(String s1, String s2) {
int m = s1.length();
int n = s2.length();
int[][] dp = new int[m + 1][n + 1];
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
if (i == 0 || j == 0) {
dp[i][j] = 0;
} else if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
int lcsLength = dp[m][n];
char[] lcs = new char[lcsLength];
int i = m, j = n;
while (i > 0 && j > 0) {
if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
lcs[lcsLength - 1] = s1.charAt(i - 1);
lcsLength--;
i--;
j--;
} else if (dp[i - 1][j] > dp[i][j - 1]) {
i--;
} else {
j--;
}
}
return new String(lcs);
}
Powyższy fragment kodu źródłowego oblicza najdłuższy wspólny podciąg między dwoma ciągami znaków text1 i text2 przy użyciu programowania dynamicznego. Algorytm przechodzi przez oba ciągi znaków, tworząc tablicę dp do przechowywania wyników pośrednich. Następnie odtwarza najdłuższy wspólny podciąg, korzystając z tablicy dp.
➡ ZOBACZ 👉: String – konwertowanie i zamiana typów: Array, ArrayList, Char, Int, Integer
Najdłuższy wspólny prefiks | Najdłuższy wspólny podciąg – podsumowanie
Powyższe algorytmy mogą być użyteczne w rozwiązywaniu problemów, takich jak porównywanie dokumentów tekstowych, analiza sekwencji DNA, zarządzanie wersjami oprogramowania i wiele innych. Dodatkowo [LCS] może być stosowany w różnych dziedzinach, w których istnieje potrzeba znajdowania wspólnych sekwencji między dwoma ciągami znaków. Algorytm ten jest efektywny i ma wiele praktycznych zastosowań.
20+ BONUSOWYCH materiałów z programowania
e-book – „8 rzeczy, które musisz wiedzieć, żeby dostać pracę jako programista”,
e-book – „Java Cheat Sheet”,
checklista – „Pytania rekrutacyjne”
i wiele, wiele wiecej!



