Thuật toán Manacher dùng để tìm đoạn con đối xứng dài nhất trong xâu. Một số bài tập về xâu có thể tham khảo. Vì sự học luôn khó khăn, mình tải về rồi Edit lại cho mọi người có thể tham khảo nâng cao sự hiểu biết của mình. Hi vọng được chia sẻ, nâng cao kiến thức cùng mọi người
Trang 1MỘT SỐ THUẬT TOÁN TÌM XÂU CON ĐỐI XỨNG DÀI NHẤT
I Mở đầu
Xâu đối xứng (xâu Palindrome) là xâu đọc từ trái sang phải hay đọc từ phải qua trái đều giống nhau, ví dụ: xâu aba, aaa, abccba… Trên thực tế, chúng ta hay gặp các bài toán có liên quan đến xâu đối xứng ở các mức độ phức tạp khác nhau Để giải quyết chúng cũng
có một số thuật toán được áp dụng, Tuy nhiên, lựa chọn thuật toán nào là một vấn đề cần quan tâm
Trong bài viết này, tôi xin trình bày một số thuật toán về tìm xâu con đối xứng dài nhất, đặc biệt là thuật toán tìm xâu con đối xứng dài nhất trong thời gian tuyến tính với các phân tích cụ thể về độ phức tạp tính toán trong mỗi thuật toán Mục đích, để chúng ta có cái nhìn hệ thống và đưa ra cách làm tối ưu trong các bài toán cụ thể
Chúng ta cùng xem lại một bài toán quen thuộc trong tin học như sau:
Bài toán: Cho một xâu S, tìm xâu con (một dãy các ký tự liên tiếp) dài nhất của S là đối
xứng
Dữ liệu vào: PALIN.INP
Gồm một dòng chứa duy nhất một xâu S
Dữ liệu ra: PALIN.OUT
Ghi độ dài xâu con dài nhất của S là đối xứng
Ví dụ:
II Nội dung
Đặt n=length(S)
1.Thuật toán duyệt toàn bộ (thời gian O(n3), bộ nhớ O(1))
Rõ ràng xâu S có n(n-1)/2 xâu con, với mỗi xâu con chúng ta lại kiểm tra xem nó có đối xứng không Độ phức tạp của thuật toán là O(n3)
Dưới đây trình bày hàm IsPalin(i,j:longint) trả về giá trị True nếu đoạn S[i j] là đối xứng
và trả về giá trị False trong trường hợp ngược lại
Function IsPalin(i,j:longint):boolean;
var k:longint;
begin
for k:=0 to (j-i+1)div 2 do
if s[i+k]<>s[j-k] then exit(False);
Exit(True);
end;
Thủ tục Process xét mọi xâu con của S xem nó có đối xứng không, nếu có thì cập nhật lại kết quả longPalin: độ dài xâu con đối xứng dài nhất, dau, cuoi: vị trí bắt đầu và kết thúc của xâu con đối xứng dài nhất
procedure process;
var i,j:longint;
begin
LongPalin:=1;
cuoi:=1;
cuoi:=1;
Trang 2for i:=1 to n-1 do
for j:=n downto i+1 do
if IsPalin(i,j) then
if j-i+1>longPalin then
begin
Longpalin:=j-i+1;
dau:=i;
cuoi:=j;
end;
end;
2 Thuật toán quy hoạch động (thời gian O(n2), bộ nhớ O(n2))
Gọi L[i,j] là độ dài xâu con lớn nhất của đoạn từ s[i j]
- Khởi tạo: L[i,j]=0 với mọi i<>j, L[i,i]=1
- Công thức:
Nếu s[i]=s[j] thì L[i,j]=L[i+1,j-1]+2
Nếu s[i]<>s[j] thì L[i,j]=max(L[i,j-1], L[i+1,j])
- Đáp số: L[1,n]
Chương trình:
procedure optimize;
var i,j:longint;
begin
fillchar(L,sizeof(L),0);
for i:=1 to n do L[i,i]:=1;
for i:=n-1 downto 1 do
for j:=i+1 to n do
if s[i]=s[j] then L[i,j]:=L[i+1,j-1]+2
else
L[i,j]:=max(L[i+1,j],L[i,j-1]);
end;
3 Thuật toán duyệt trung tâm (thời gian O(n2), bộ nhớ O(n))
Dễ dàng nhận thấy vị trí trung tâm của một xâu con đối xứng chỉ có thể là 1 ký tự hoặc 1 chỗ trống Như vậy, xâu S có 2n+1 trung tâm, với mỗi trung tâm đó, ta tìm xâu con dài nhất là đối xứng bằng cách xuất phát từ mỗi trung tâm, đồng thời phát triển đồng thời về bên trái và bên phải cho đến khi tìm được xâu con đối xứng dài nhất tại trung tâm đó Độ phức tạp thuật toán là O(n2)
Gọi P[i] là độ dài xâu con đối xứng dài nhất nhận vị trí i làm trung tâm, khai báo P:array[0 2*n] of longint
Chương trình:
procedure xuly;
var i,dau,cuoi:longint;
begin
n:=length(s);
m:=2*n;
fillchar(P,sizeof(P),0);
P[1]:=1; P[m-1]:=1;
max:=1;//max: độ dài xâu con đối xứng dài nhất
Trang 3e1:=1;//e1: vị trí kết thúc của xâu con đối xứng dài nhất
s1:=1;// s1: vị trí bắt đầu của xâu con đối xứng dài nhất
for i:=2 to m-2 do
begin
if (i mod 2=0) then
begin
dau:=i div 2;
cuoi:=dau+1;
end
else
begin
P[i]:=1;
dau:=(i div 2) ;
cuoi:=dau+2;
end;
while (dau>=1) and (cuoi<=n) and (s[dau]=s[cuoi]) do
begin
dau:=dau-1;
cuoi:=cuoi+1;
end;
P[i]:=cuoi-dau+1-2;
if P[i]>max then
begin
max:=P[i];
e1:=cuoi-1;
s1:=dau+1;
end;
end;
end;
4 Thuật toán Manacher (thời gian O(n), bộ nhớ O(n))
Có cách nào tính mảng P (ở thuật toán duyệt trung tâm) nhanh hơn không?
Xét xâu S chứa nhiều xâu đối xứng chồng chéo, ví dụ: “aaaaaaaaa” và
“cabcbabcbabcba” Thực tế, ta có thể tận dụng được ưu điểm của tính đối xứng này và tránh được các tính toán không cần thiết Cụ thể ta làm như sau:
Đầu tiên, ta thay xâu S bằng xâu T bằng cách thêm ký tự “#” vào các chỗ trống trên xâu
S, ví dụ: S = “abaaba” thì T = “#a#b#a#a#b#a#” Để tìm xâu con đối xứng dài nhất của S,
ta cần mở rộng mỗi Ti về hai phía 1 khoảng d dài nhất sao cho Ti-d Ti+d là đối xứng d được gọi là bán kính của xâu con đối xứng nhận Ti làm trung tâm
Gọi P[i] là độ dài xâu con đối xứng dài nhất nhận Ti làm trung tâm, đáp số của bài toán là max(P[i]), i=0 2*n Với ví dụ trên, mảng P tương ứng là:
T=#a#b#a#a#b#a#
P=0103016103010
Nhận thấy, độ dài xâu con đối xứng dài nhất là “abaaba”, tại vị trí P6=6
Xét ví dụ khác, S= “babcbabcbaccba”, giả sử chúng ta đang ở trạng thái sau:
Trang 4C là vị trí trung tâm, L và R là vị trí mở rộng tối đa quanh trung tâm C, nghĩa là đoạn T[L R] đối xứng có tâm là TC Ta đã tính được mảng P như trên, làm thế nào để tính tiếp P[i] một cách hiệu quả?
Với i=13, i’ là vị trí đối xứng của i qua tâm C, P[ i' ] = P[ 9 ] = 1 Rõ ràng P[i]=1 do tính đối xứng của xâu Tương tự, ta tính được P[ 12 ] = P[ 10 ] = 0, P[ 13 ] = P[9 ] = 1, P[ 14 ]
= P[ 8 ] = 0
Tiếp theo, với i=15, theo tính chất đối xứng thì P[15]=P[7]=7 còn đúng không? Rõ ràng
là sai, vì trong trường hợp này, khi mở rộng quanh trung tâm T15 ta được xâu con đối xứng là “a#b#c#b#a”, nghĩa là P[15]=5, tại sao?
Trên hình vẽ 2 đường thẳng biểu diễn cho 2 xâu con đối xứng dài nhât quanh trung tâm i’ (đường bên trên) và xâu con đối xứng dài nhất quanh trung tâm i, mà i và i’ đối xứng nhau qua tâm C, đường xanh nét liền thể hiện tính đối xứng quanh tâm C, đường xanh nét đứt thể hiện tính đối xứng đi xuyên qua tâm C, đường nét liền màu đỏ thể hiện ta có thể
mở rộng tối đa về 2 phía thỏa mãn tính đối xứng của xâu con
Ta có: P[i’]=7, và P[i]≥5 (do tính đối xứng), trong khi vị trí đang xét là R=20, muốn tính được P[i] ta phải mở rộng sang phải của R, nghĩa là phải đi so sánh T[21] và T[9], mà T[21]≠T[9] nên P[i]=5 Từ đó, ta có một phần thuật toán như sau:
if P[ i' ] ≤ R – i,
then P[ i ] ← P[ i' ]
else P[ i ] ≥ P[ i' ] (khi đó ta có thể mở rộng về bên phải R để tìm P[i]).
Thuật toán Manacher tìm xâu con đối xứng dài nhất của xâu S:
- B1: Tính T là mở rộng của xâu S bằng cách thêm ký tự ‘#’ vào các chỗ trống
- B2: Đặt n length(T)
C:=1; // C là vị trí trung tâm hiện tại
R:=1; // R là vị trí đang xét về bên phải trung tâm hiện tại C
Res := 0; // Res là độ dài xâu con đối xứng dài nhất của xâu S
- B3: For i:=2 to n do
Tính i’:=2*C-i; // I’ là vị trí đối xứng về bên trái trung tâm C;
Nếu i // mở rộng bán kính đối xứng quanh trung tâm i
while (T[i-1-P[i]]=T[i+1+P[i]]) do P[i] P[i]+1;
// nếu mở rộng thành công thì đặt lại trung tâm mới là I, bán kính mở rộng về bên phải R
= i+P[i]
Nếu i+P[i]>R thì: C :=i; R:=i+P[i];
Nếu res < P[i] thì Res P[i]; // cập nhật lại kết quả
Chương trình:
// T là xâu mở rộng của S
T:='#';
for i:=1 to n do
begin
T:=T+s[i]+'#';
end;
n:=length(T);
C:=1; // C là vị trí trung tâm hiện tại
R:=1; // R là vị trí đang xét về bên phải trung tâm C
res:=0; // res: Độ dài xâu con đối xứng dài nhất
P[1]:=1;
for i:=2 to n do
Trang 5begin
i1:=2*C-i; // i1 là vị trí đối xứng về bên trái trung tâm C
if (i1>0) and (R>i) then P[i]:=min(R-i, P[i1]) else P[i]:=0;
while ((i-P[i]-1)>0) and ((i+P[i]+1)<=n) and (T[i-1-P[i]]=T[i+1+P[i]]) do P[i]:=P[i]+1;// mở rộng bán kính đối xứng quanh trung tâm i
if i+P[i]>R then // nếu mở rộng thành công thì đặt lại trung tâm mới là i
begin
C:=i;
R:=i+P[i];
end;
if res <P[i] then Res := P[i] ; //Cập nhật lại kết quả end;
Đánh giá thuật toán: Thuật toán gồm 1 vòng lặp while lồng trong vòng lặp for i bên
ngoài, còn lại các lệnh khác đều có độ phức tạp O(1) Nhưng nhìn tổng thể, mỗi P[i] chỉ được tính 1 lần, mỗi T[i] chỉ được/bị so sánh 1 lần, với i=0 2n, nên tổng độ phức tạp của thuật toán là O(n)
III Bài tập áp dụng
1 Chuỗi đối xứng (mã bài NKPALIN)
Tìm một chuỗi con đối xứng dài nhất của một chuỗi s cho trước Chuỗi con là chuỗi thu được khi xóa đi một số ký tự từ chuỗi ban đầu
Dữ liệu vào
Gồm một dòng duy nhất chứa chuỗi s, chỉ gồm những chữ cái in thường
Kết qủa
Gồm một dòng duy nhất là một xâu con đối xứng dài nhất của xâu s Nếu có nhiều kết quả, chỉ cần in ra một kết quả bất kỳ
Giới hạn
Chuỗi s có độ dài không vượt quá 2000
Thuật toán: Quy hoạch động
Gọi L[i,j] là số ký tự ít nhất cần xóa để đoạn S[i j] đối xứng
Công thức:
Nếu S[i]=S[j] thì L[i,j]=L[i+1,j-1]
Nếu S[i]<>S[j] thì L[i,j]=min(L[i+1,j], L[I,j-1]+1) với i=n-1 1,j=i+1 n
Khởi tạo: L[i,i]=0, i=1 n
Đáp số: n-L[1,n], độ phức tạp O(n2)
2 PALINDROME dài nhất (mã bài PALINY)
Cho xâu S Tìm xâu đối xứng dài nhất gồm các kí tự liên tiếp trong S
Input
Dòng 1: N (số ký tự của xâu S; N<=50 000)
Dòng 2: Xâu ký tự độ dài N
Output
1 dòng duy nhất gồm độ dài của xâu đối xứng dài nhất
Thuật toán: Với n lớn, áp dụng thuật toán Manacher trong mục II.4 độ phức tạp O(n)
3 Chia thành ít xâu đối xứng
Cho xâu S Chia xâu S thành ít xâu con đối xứng nhất
Input
Xâu S có độ dài ≤ 1000
Trang 6Số xâu con đối xứng ít nhất mà S chia được
Thuật toán: Quy hoạch động
Gọi F[i] là số xâu con ít nhất đoạn 1 i chia được
L[I,j]=true nếu đoạn i j đối xứng
Công thức:
+Khởi tạo mảng L: L[i,i]=true;
+Nếu S[i]=S[j] thì L[i,j]=L[i+1,j-1]
Ngược lại: L[i,j]=false; với i=n-1 1, j=i+1 n
+Khởi tạo mảng F: F[i]=i;
+F[i]=min(F[i], F[j]+1) nếu đoạn [j+1 i] đối xứng (hay L[j+1,i]=true) với
i=1 n, j=i-1 0
Độ phức tạp O(n2)
4 Palindrome - IOI 2000
Cho một xâu, hỏi phải thêm vào nó ít nhất bao nhiêu xâu kí tự để nó trở thành một
xâu đối xứng (độ dài <= 500)
Input
- Dòng đầu là số T (T≤10, số bộ test)
- T dòng sau, mỗi dòng chứa 1 xâu S có độ dài ≤ 500
Output
Gồm nhiều dòng, mỗi dòng là một xâu đối xứng tương ứng với xâu trên file Input
Thuật toán:
Làm tương tự bài 2, đáp số L[1,n] là số ký tự cần thêm, căn cứ vào công thức để truy vết
Độ phức tạp O(n2)
Bài tập tự luyện:
5 Xâu đối xứng (mã bài NKSP)
Bạn được cho N xâu, như vậy sẽ có NxN cặp xâu Bạn hãy đếm xem trong NxN cặp xâu này, có bao nhiêu cặp mà khi nối xâu thứ hai vào sau xâu thứ nhất sẽ cho ra một xâu đối xứng
Input
Dòng đầu ghi một số N N dòng sau mỗi dòng mô tả một xâu, bắt đầu là độ dài của xâu, sau đó là một dấu cách và tiếp theo là nội dung của xâu.(Xâu chỉ gồm các chữ cái latin thường và có độ dài nguyên dương)
Dữ liệu vào luôn đảm bảo tổng độ dài các xâu không quá 1000000
Output
Ghi ra một số duy nhất là số cặp xâu tìm được
6 Xâu đối xứng (Mã bài PALINX)
Một xâu được gọi là đối xứng nếu đọc từ trái qua phải và đọc từ phải qua trái đều giống nhau
Ví dụ xâu "aba", "abba" là xâu đối xứng; còn xâu "abc", "abca" thì không
Bạn được cho N xâu, như vậy sẽ có NxN cặp xâu Bạn hãy đếm xem trong NxN cặp xâu này, có bao nhiêu cặp mà khi nối xâu thứ hai vào sau xâu thứ nhất sẽ cho ra một xâu đối xứng
Input
Dòng đầu ghi một số N N dòng sau mỗi dòng mô tả một xâu, bắt đầu là độ dài của xâu, sau đó là một dấu cách và tiếp theo là nội dung của xâu.(Xâu chỉ gồm các chữ cái latin thường và có độ dài nguyên dương)
Trang 7Dữ liệu vào luôn đảm bảo tổng độ dài các xâu không quá 1000000.
Output
Ghi ra một số duy nhất là số cặp xâu tìm được
IV Kết luận
Trong bài viết này, tôi trình bày 4 thuật toán tìm xâu con đối xứng dài nhất, từ thuật toán
có độ phức tạp lớn đến thuật toán có độ phức tạp nhỏ Tuy nhiên, chúng đều là các thuật toán dễ hiểu, dễ cài đặt Thực tế hiện nay có đến 6 thuật toán tìm xâu con đối xứng dài nhất, ngoài 4 thuật toán nêu trên còn có thuật toán Hash và thuật toán sử dụng suffix trees với độ phức tạp O(nlogn) mà tôi không trình bày ở đây Tôi cho rằng, tùy theo từng bài toán cụ thể, các bạn có thể chọn cho mình một thuật toán phù hợp và thuật toán Manachar
là thuật toán hiệu quả nhất cả về mặt thời gian, bộ nhớ và cài đặt, là công cụ tốt chúng ta nên trang bị cho học sinh để các em có thêm cách nhìn, cách tư duy, tự tin khi tham gia các kỳ thi
Tài liệu tham khảo:
1.http://leetcode.com/2011/11/longest-palindromic-substring-part-i.html
2 http://leetcode.com/2011/11/longest-palindromic-substring-part-ii.html
3 http://www.akalin.cx/longest-palindrome-linear-time
4 vnoi.info
Lê Thị Hải Hằng
Tài liệu liên quan
Nghiên cứu tính toán lưới và thử nghiệm một số thuật toán lý thuyết đồ thị
Nghiên cứu tính toán lưới và thực nghiệm trên một số thuật toán lý thuyết đồ thị
Nghiên cứu tính toán lưới thử nghiệm một số thuật toán lí thuyết đồ thị
TRONG HỆ THỐNG TÌM KIẾM ẢNH LỚP TRÊN THỬ NGHIỆM
TRONG HỆ THỐNG TÌM KIẾM ẢNH LỚP TRÊN THỬ NGHIỆM
BÁO CÁO TỐT NGHIỆP: TÌM HIỂU VÀ ĐÁNH GIÁ MỘT SỐ THUẬT TOÁN TÌM KIẾM TRUYỀN THỐNG ỨNG DỤNG TRONG TIN HỌC pdf
Một số thuật toán tìm đường đi ngắn nhất và xây dựng ứng dụng GAME PIKACHU
tiểu luận một số thuật toán cặp ghép trên đồ thị hai phía
nghiên cứu và thử nghiệm một số thuật toán phát hiện các đồ thị con thường xuyên
Trang 8 Một số thuật toán tìm chuỗi và xây dựng chương trình minh họa thuật toán boyer moore