Fundamentalnym zagadnieniem bioinformatyki (ale nie tylko) jest problem znalezienia optymalnego ułożenia dwóch lub więcej sekwencji pewnych elementów (symboli, liter, itp) . W typowym przypadku sekwencji DNA mamy do czynienia z sekwencjami czterech symboli: “C”, “G”, “T” i “A”. W przypadku sekwencji białkowych zbiorem symboli są oznaczenia (najczęściej jednoliterowe, ze względu na minimalną wielkość identyfikatorów) 20 aminokwasów, plus dodatkowe symbole jak np. “X” oznaczający nieokreślony element.
Poniżej podano 2 losowo wygenerowane sekwencje ,,DNA’’, zapisane jako zmienne o nazwach s1 i s2, które posłużą jako przykłady.

(* s1=RandomChoice[{"C","G","T","A"},100]//StringJoin *)

s1="ACTGCATGTCAGTAGCTGCATGATCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAGTATGTGCATGCTAGCT"

(* s2=StringDrop[StringDrop[s1,30],-30] *)

s2="AAAAAAAAAAAAACAAAAAAAAAAAAAAAA"

W najprymitywniejszej postaci, przez dopasowywanie sekwencji rozumiemy proces przesuwania jednej sekwencji względem drugiej (w sytuacji gdy zapisane są jedna pod drugą) do momentu w którym uznamy, że pewne możliwie najdłuższe fragmenty są identyczne. W najprostszych przypadkach optymalne ułożenie jest często ,,oczywiste’’. Jednak już przy próbie prostego wypisania jednej sekwekcji nad drugą natrafiamy na pierwszy problem:

{{s1},{s2}}//TableForm

Otóż sekwencje które porównujemy mają na ogół inne długości, na czym więc polega porównywanie ,,nadmiarowych’’ fragmentów z ,,niczym’’? Eleganckie rozwiązanie tego zagadnienia podane zostało w pracy P. Sellersa, SIAM J. Appl. Math. 26 (787) 1974. 
Po pierwsze wprowadzamy do zbioru symboli element neutralny, oznaczany zwykle w bioinformatyce jako myślnik (pozioma kreska, znak minus ,,-“).
Po drugie wprowadzamy klasę równoważności sekwencji, które różnią się tylko i wyłącznie o elementy neutralne, np: “ACGT-“, “A-C-G-T”, “------------ACGT”, “-------AC------GT--------“, są różnymi reprezentacjami tej samej sekwencji “ACGT”.  W szczególności można teoretycznie przyjąć, że każda sekwencja jest formalnie nieskończonej długości, zawierając dowolnie dużo elementów neutralnych przed i za właściwą badaną sekwencją. W praktyce nie dodaje się tych elementów dowolnie dużo, a tylko tyle aby dowolne wzajemne ustawienia były możliwe. W przypadku dwóch sekwencji możemy np. rozszerzyć dłuższą z sekwencji (s1 w przykładzie) o tyle elementów neutralnych z przodu i z tyłu ile zawiera sekwencja krótsza (długość sekwencji s2).

W tym celu tworzymy funkcję o nazwie  przerwa, która zwraca łańcuch tekstowy składający się z n elementów neutralnych “-“:

przerwa[n_]:=Nest[StringJoin["-",#]&,"-",n-1] 
(* KOMENTARZ:
Definicja funkcji: wywołuje n-1 razy ,,czystą funkcję'' StringJoin["-",#]& której zadaniem jest doklejenie (StringJoin) znaku "-"
n-1 razy do znaku "-", co daje n znaków "-"
*)

przerwa[10] (* sprawdzenie działania funkcji przerwa *)

StringLength[%] (* sprawdzenie, czy po podaniu argumentu 10 faktycznie otrzymujemy 10 znaków "-"*)

Zgodnie z tym co napisałem wyżej, rozszerzam sekwencję s1 o tyle elemntów neutralnych z przodu i z tyłu ile wynosi długość sekwencji s2:

s1r=przerwa[StringLength[s2]]<>s1<>przerwa[StringLength[s2]]

Aby łatwiej było manipulować sekwencjami zmieniam je na listy o nazwach s1l i s2l:

s1l=Characters[s1r]

s2l=Characters[s2]

Poniższy mainpulator pozwala przesuwać sekwencję s1 względem sekwencji s2. Dosyć ewidentne jest, że optymalne ułożenie pojawia się w okolicy ali=60:

Manipulate[
{
Table[s1l[[i+ali]],{i,1,StringLength[s2]}],
Table[s2l[[i]],{i,1,StringLength[s2]}]
}//Grid,
{ali,1,StringLength[s1]+StringLength[s2],1}]

Ocenianie ,,na oko’’ może być skuteczne w najprostszych przypadkach. Liczne powody wymuszają sprecyzowanie pojęcia dopasowania:
1) ocena musi być powtarzalna
2) różne osoby (instytucje itp.) powinny mieć możliwość porównania i powtórzenia wyników
3) w przypadku długich sekwencji ocena wizualna staje się niemożliwa lub pracochłonna
4) istnieje potrzeba automatycznego dopasowywania setek i tysięcy sekwecji
5) numeryczna wartośc oceny podaje miarę jakości dopasownia
6) programy komputerowe dopasowujące sekwencje opierają się na znajdywaniu ekstremum (minimum lub maksimum) oceny w zależności od dopasowania

Konkretne numeryczne wartości które przypisujemy poszczególnym parom symboli są tematem prac naukowych. W najprostszym przykładzie poniżej przyjęto:
1) ocena wynosi 1 jeżeli symbole znajdujące się jeden nad drugim są identyczne, ale żaden z nich nie jest symbolem neutralnym
2) ocena wynosi zero, jeżeli symbole są różne, lub jeden z nich jest symbolem neutralnym

ocena[ch1_,ch2_]:=If[ch1==ch2&&ch1≠"-"&&ch2≠"-",1,0]

ocena["-","-"]

ocena["-","A"]

ocena["A","A"]

ocena["A","G"]

Typowym sposobem wizualizacji funkcji oceniającej jest macierz:

symb={"A","C","G","T","-"}

tbl=Table[ocena[symb[[i]],symb[[j]]],{i,1,5},{j,1,5}]

Table[ocena[symb[[i]],symb[[j]]],{i,1,5},{j,1,5}]//MatrixForm

Lub w bardziej czytelnej postaci:

Join[{Join[{"/"},symb]},Join[{symb},tbl]//Transpose]//TraditionalForm

Funkcja wartosc poniżej dodaje oceny dla wszystkich mozliwych par dla sekwencji s1 i s2 przesuniętych względem siebie o ilość pozycji ali:

wartosc[s1_,s2_,ali_?NumberQ]:=Module[{se1,se2,i},
se1=przerwa[StringLength[s2]]<>s1<>przerwa[StringLength[s2]]//Characters;
se2=s2//Characters;
Sum[ocena[se1[[i+ali]],se2[[i]]],{i,1,StringLength[s2]}]
]

Poniższe przykłady pokazują, że przesunięcie sekwencji s2 o 60 pozycji względem początku s1 daje znacznie wyższą wartość dopasowania niż przesunięcie o 6 pozycji. W tym właśnie sensie pierwsze ułożenie jest lepsze niż drugie. Dopasowaniem sekwencji  nazywamy takie ich  ułożenie (przesunięcie, ang. alignment) dla którego suma ocen przyjmuje największą możliwą wartość.

wartosc[s1,s2,60]

wartosc[s1,s2,6]

Aby wyznaczyć wartość przesunięcia ali dla którego sumaryczna ocena będzie maksymalna musimy zbadać wszystkie możliwe ułożenia:

Table[wartosc[s1,s2,k],{k,0,StringLength[s1]+StringLength[s2]}]

Ewidentnie, najwyższa ocena wynosi 29. Jak widać istnieje kilka dopasowań o identycznej ocenie - są one równie dobre wg. założonych kryteriów. Poniższy wykres pokazuje jak ocena początkowo rośnie, a następnie maleje:

ListPlot[%,Joined->True,InterpolationOrder->0]

Poniższy manipulator ilustruje działanie całej procedury opisanej szczegółowo powyżej. Zachęcam do wypróbowania metody dla innych sekwencji (Uwaga: założono, że sekwencja s1 jest dłuższa niż s2).

Manipulate[
{{"OCENA: SUMA DOPASOWAN="<>ToString[wartosc[s1,s2,ali]]},
{MatrixForm[
{
Table[ocena[s1l[[i+ali]],s2l[[i]]],{i,1,StringLength[s2]}],
Table[s1l[[i+ali]],{i,1,StringLength[s2]}],
Table[s2l[[i]],{i,1,StringLength[s2]}]
}]
}}//TableForm,
{ali,1,StringLength[s1]+StringLength[s2],1}]

Poniższe polecenie najpierw generuje wszystkie możliwe oceny w zależności od przesunięcia (Table), następnie sortuje je od najmniejszej do największej (Sort), potem odwraca (Reverse) kolejność (interesują nas najwyższe wartości, więc chcemy aby były na początku a nie na końcu). Na koniec wybieramy najlepsze, czyli pierwsze na liście (First). Pierwsza wartość podaje ocenę, druga wartość przesunięcia.

Table[{wartosc[s1,s2,k],k},{k,0,StringLength[s1]+StringLength[s2]}]//Sort//Reverse//First

PODSUMOWANIE:
1) najprostszym sposobem dopasowywania sekwencji jest ich przesuwanie względem siebie
2) aby umożliwić porównywanie sekwencji o różnych długościach (a także aby wprowadzić przerwy w bardziej realistycznych metodach) wprowadzamy element neutralny (oznaczany zwykle jako “-“)
3) wszystkie sekwencje które różnią się tylko i wyłącznie o elementy neutralne (czyli po ich usunięciu stają się tożsame) traktujemy jako elementy należące do tej samej klasy równoważności; w szczególności można je traktować jako rózne ,,reprezentacje’’ tej samej sekwencji (formalnie nieskończonej, ale zawierającej skończoną ilość elementów nietrywialnych, różnych od neutralnego)
4) tworzymy (lub wybieramy gotową)  macierz podobieństwa (ang. similarity matrix, similarity rules), czyli funkcję która podaje jak oceniamy dopasowanie par elementów, uwzględniając elementy neutralne
5) obliczamy sumę ocen par elementów dla wszystkich możliwych ustawień
6) wybieramy to ustawienie, które zostało najwyżej ocenione

Algorytm opisany powyżej wymaga co obliczenia ocen jedynie w n1+ 2 n2 przypadkach, co nawet dla sekwecji o długościach rzędu tysięcy nie zajmuje komputerowi więcej niż ułamek mikrosekundy.

Bardziej realistyczne dopasowanie sekwencji wymaga wprowadzenie przerw (ang. gap), czyli elementów neutralnych nie tylko na końcach, ale także wewnątrz każdej sekwecji. Powoduje to, że ilość możliwych ustawień rośnie dramatycznie (jak bardzo?). Wymaga to radykalnej zmiany podejścia, gdyż obliczenie oceny wszystkich dopasowań jedno po drugim zajęłoby bardzo dużo czasu, przynajmniej dla dłuższych niż kilkanaście elementów sekwencji. W praktyce konieczna może też być też zmiana sposobu wartościowania elementów neutralnych, np. poprzez dodatkową karę (ang. penalty) za występujęce po sobie liczne elementy neutralne (duże przerwy). Kara ta może być zarówno dodatnia jak i ujemna.