Sáng tạo trong thuật toán và lập trình với ngôn ngữ Pascal và C# - Tập 1 - Chương 4: Tổ chức dữ liệu
Giả sử xâu s chứa biểu thức cần xử lí. Ta duyệt lần lượt từ đầu đến cuối xâu s, với
mỗi kí tự s[i] ta xét hai trường hợp:
Trường hợp thứ nhất: s[i] là dấu mở ngoặc '(': ta ghi nhận i là vị trí
xuất hiện đầu cụm vào một ngăn xếp (stack) st:
inc(p); st[p] := i;
trong đó p là con trỏ ngăn xếp. p luôn luôn trỏ đến ngọn, tức là phần tử cuối
cùng của ngăn xếp. Thủ tục này gọi là nạp vào ngăn xếp: NapST.
t tổ chức hậu tố như sau.
Hậu tố của một xâu là đoạn cuối của xâu đó. Như vậy một xâu có chiều dài n sẽ có
n hậu tố. Thí dụ, với xâu mẫu s[1..10] = 'cabxabcdab' ta có 10 hậu tố sau
đây:
s[1..10] = 'cabxabcdab'
s[2..10] = 'abxabcdab'
s[3..10] = 'bxabcdab'
s[4..10] = 'xabcdab'
s[5..10] = 'abcdab'
s[6..10] = 'bcdab'
s[7..10] = 'cdab'
s[8..10] = 'dab'
s[9..10] = 'ab'
s[10..10] = 'b'
Như vậy, hậu tố sau sẽ nhận được từ hậu tố sát trước nó bằng cách bỏ đi kí tự
đầu tiên.
Trước hết ta sắp tăng các hậu tố của xâu mẫu s theo trật tự từ điển. Sử dụng một
mảng chỉ dẫn id, trong đó id[i] trỏ đến vị trí đầu tiên của hậu tố trong xâu mẫu. Cụ thể
là, nếu id[i] = k thì hậu tố tương ứng sẽ là s[k..n]. Sau khi sắp tăng các hậu tố của xâu
mẫu s[1..10] = 'cabxabcdab' ta thu được:
i id[i] Hậu tố Xâu
1 9 S[9..10] ab
2 5 S[5..10] abcdab
3 2 S[2..10] abxabcdab
4 10 S[10..10] b
5 6 S[6..10] bcdab
6 3 S[3..10] bxabcdab
7 1 S[1..10] cabxabcdab
8 7 S[7..10] cdab
9 8 S[8..10] dab
10 4 S[4..10] xabcdab
Sắp tăng theo chỉ dẫn các hậu tố của xâu
s[1..10] = 'cabxabcdab'
Việc còn lại là so sánh xâu x với các hậu tố s[i..j] để tìm khúc đầu chung dài nhất
giữa chúng. Thí dụ, với x[1..4]='abcd' thì khúc đầu chung dài nhất tìm được với
hậu tố s[5..10] do id[2] trỏ tới. Vị trí v tìm được sẽ là 5 và chiều dài lớn nhất d sẽ là 4.
Phần chính của chương trình sẽ như sau:
procedure Run;
begin
...
n:= length(s);
Sáng tạo trong Thuật toán và Lập trình Tập I
122
for i:=1 to n do id[i]:=i;
IdQuikSort(1,n);
while not seekeof(f) do
begin
readln(f,x);
writeln(g,x);
Tim; {xac dinh v và d}
writeln(g,v,BL,d);
end;
end;
Để ý rằng với mỗi xâu x, nếu phần tử đầu tiên của x là x[1] không trùng với phần tử
đầu tiên của hậu tố h thì chiều dài của khúc đầu chung của chúng sẽ bằng 0. Nhờ nhận
xét này và do dãy các hậu tố đã được sắp tăng nên với mỗi xâu x, trước hết ta gọi hàm
Binsearch để thực hiện tìm kiếm nhị phân phần tử x[1] trong dãy gồm các phần tử
đầu tiên của các hậu tố, sau đó ta thực hiện việc duyệt tìm.
procedure Tim;
var
i,Len: integer;
begin
v:=BinSearch; d := 0;
if v=0 then exit;
Maxlen:=0;
for i:=v to n do
begin
if s[id[i]]x[1] then exit;
Len:=Eqsx(id[i]);
if Len > d then
begin
d:=Len;
v:=id[i];
end;
end;
end;
Hàm BinSearch sẽ cho ra chỉ dẫn tới hậu tố h đầu tiên thoả điều kiện h[1] =
x[1].
(*---------------------------------
Tim xuat hien cua x[1]trong day
da sap cac hau to
---------------------------------*)
function BinSearch:integer;
var
d,c,m: integer;
begin
d:=1;
c:=n;
repeat
m:=(d+c) div 2;
if x[1]>s[id[m]] then d:=m+1
else c:=m;
until d=c;
Sáng tạo trong Thuật toán và Lập trình Tập I
123
if x[1]s[id[d]] then Binsearch := -1
else BinSearch := d;
end;
Hàm Eqsx(i) cho ta chiều dài lớn nhất của khúc đầu chung giữa hậu tố s[i..n] và
xâu x.
(*---------------------------------
Khuc dau dai nhat giua
hau to s[i..n] va x
---------------------------------*)
function Eqsx(i:integer): integer;
var
k,m:integer;
begin
m:=min(length(x),n-i+1);
for k:=1 to m do
if s[i+k-1]x[k] then
begin
Eqsx:=k-1;
exit;
end;
Eqsx:=m;
end;
(* Pascal *)
(*----------------------
STRINGS: Xau mau
------------------------*)
program Strings;
{$B-}
uses crt;
const
MN = 255;
cd = #0; {ki tu trong}
cc = #255; {ki tu cuoi cua bang ma ASCII}
BL=#32; {dau cach}
fn = 'strings.inp'; {tep vao}
gn = 'strings.out'; {tep ra}
type
mb1 = array[0..MN] of integer;
var
f,g: text;
s,x: string; {s: xau mau}
id: mb1; {chi dan}
n: integer; {chieu dai xau mau s}
v,d: integer;
{v: vi tri xuat hien khuc dau cua x trong xau mau s}
{d: maxlen}
(*-------------------------------
min cua 2 phan tu
--------------------------------*)
Sáng tạo trong Thuật toán và Lập trình Tập I
124
function min(a,b:integer):integer;
begin
if a<=b then min:=a
else min:=b;
end;
(*-------------------------------
Tim xuat hien cua x[1] trong day da sap cac hau to
-------------------------------*)
function BinSearch:integer;
var
d,c,m: integer;
begin
d:=1;
c:=n;
repeat
m:=(d+c) div 2;
if x[1]>s[id[m]] then d:=m+1
else c:=m;
until d=c;
if x[1]s[id[d]] then Binsearch:=0
else BinSearch:=d;
end;
(*--------------------------------------
so sanh 2 hau to trong s:
s[i..n] va s[j..n]
---------------------------------------*)
function sanh(i,j:integer):integer;
var k:integer;
begin
for k:=0 to min(n-i,n-j) do
if s[i+k]s[j+k] then
begin
if s[i+k]<s[j+k] then sanh:=-1
else sanh:=1;
exit;
end;
if i=j then sanh:=0
else if i<j then sanh:=1
else sanh:=-1;
end;
(*-------------------------------------
Quick sort cac hau to theo chi dan
-------------------------------------*)
procedure IdQuickSort(d,c: integer);
var i,j,m,t: integer;
begin
i:=d; {dau}
j:=c; {cuoi}
m:=id[(i+j) div 2]; {phan tu giua}
while i<=j do
begin
Sáng tạo trong Thuật toán và Lập trình Tập I
125
while sanh(id[i],m)<0 do inc(i);
while sanh(id[j],m)>0 do dec(j);
if (i<=j) then
begin
t:=id[i];
id[i]:=id[j];
id[j]:=t;
inc(i); dec(j);
end;
end;
if d<j then IdQuickSort(d,j);
if i<c then IdQuickSort(i,c);
end;
(*------------------------------------------
Khuc dau dai nhat giua hau to s[i..n] va x
-------------------------------------------*)
function Eqsx(i:integer): integer;
var k,m:integer;
begin
m:=min(length(x),n-i+1);
for k:=1 to m do
if s[i+k-1]x[k] then
begin
Eqsx:=k-1;
exit;
end;
Eqsx:=m;
end;
(*--------------------------------------------
Tim vi tri va chieu dai lon nhat
MaxLen giua cac hau to cua xau mau s va xau x
---------------------------------------------*)
procedure Tim;
var i,Len: integer;
begin
v:=BinSearch;
d:=0;
if v=0 then exit;
for i:=v to n do
begin
if s[id[i]]x[1] then exit;
Len:=Eqsx(id[i]);
if Len > d then
begin
d:=Len;
v:=id[i];
end;
end;
end;
procedure Run;
Sáng tạo trong Thuật toán và Lập trình Tập I
126
var i:integer;
begin
assign(f,fn);
reset(f);
assign(g,gn);
rewrite(g);
readln(f, s);
writeln(g,s);
n:= length(s);
for i:=1 to n do id[i]:=i;
IdQuickSort(1,n);
while not seekeof(f) do
begin
readln(f,x);
writeln(g,x);
Tim;
writeln(g,v,BL,d);
end;
close(f);
close(g);
end;
BEGIN
Run;
END.
Dữ liệu kiểm thử
STRINGS.INP
Kết quả dự kiến
STRINGS.OUT
cabxabcdab
abcd
cdaeh
cabxabcdab
abcd
5 4
cdaeh
7 3
// C#
using System;
using System.IO;
namespace SangTao1
{
/*------------------------
* Xau mau
* ----------------------*/
class Strings
{
const string fn = "strings.inp";
const string gn = "strings.out";
static string s; // xau mau
static string x; // xau can xu ly
Sáng tạo trong Thuật toán và Lập trình Tập I
127
static int[] id;
static int v = 0;// vi tri xuat hien khuc dau x trg s
static int d = 0;// chieu dai x trong s
static int n = 0; // chieu dai xau mau
static void Main()
{
Run();
Test();
Console.ReadLine();
} // Main
// Doc lai file gn de kiem tra ket qua
static void Test()
{
Console.WriteLine("\n Kiem tra lai ket qua \n\n");
Console.WriteLine("\n Input: " +
File.ReadAllText(fn));
Console.WriteLine("\n Output: " +
File.ReadAllText(gn));
}
static void Run()
{
StreamReader f = File.OpenText(fn);
StreamWriter g = File.CreateText(gn);
// Bo qua cac dong trong dau tien
while ((s = (f.ReadLine()).Trim()) == "") ;
n = s.Length; // Len xau mau
id = new int[n];
// Khoi tri cho index
for (int i = 0; i < n; ++i) id[i] = i;
IdQSort(0, n - 1);
Console.WriteLine(" Xau mau: " + s + "\n\n");
SPrint();
g.WriteLine(s);
while ((x = f.ReadLine()) != null)
{
x = x.Trim();
if (x != "")
{
Console.WriteLine(x);
g.WriteLine(x);
Find();
g.WriteLine(v + " " + d);
}
}
Sáng tạo trong Thuật toán và Lập trình Tập I
128
f.Close(); g.Close();
}
static void Find()
{
v = BinSearch(x[0]);// hau to co ki tu dau la x[0]
d = 0;
if (v < 0) return;
for (int i = v; i < n; ++i)
{
int j = id[i];
if (s[j] != x[0]) return;
int k = ComLen(x, j);
if (d < k) { v = j + 1; d = k;}
}
}
// MaxLen khuc dau cua x va hau to s[j...
static int ComLen(string x, int j)
{
int minlen = Min(x.Length, n - j);
for (int i = 0; i < minlen; ++i)
if (x[i] != s[j + i]) return i;
return minlen;
}
static int Min(int a, int b)
{ return (a < b) ? a : b;}
static int BinSearch(char c)
{
int i = 0;
int j = n - 1;
int m = 0;
while (i < j)
{
m = (i + j) / 2;
if (s[id[m]] < c) i = m + 1;
else j = m;
}
return (s[id[i]] == c) ? i : -1;
}
// Hien thi day duoc sap cac hau to
// cua s de kiem tra
static void SPrint()
{
Console.WriteLine("\n Cac hau to sap tang: \n");
for (int i = 0; i < n; ++i)
Console.WriteLine(s.Substring(id[i], n - id[i]));
Sáng tạo trong Thuật toán và Lập trình Tập I
129
}
static void IdQSort(int d, int c)
{
int i = d;
int j = c;
int m = id[(i + j) / 2];
int t = 0;
while (i <= j)
{
while (Sanh(id[i], m) < 0) ++i;
while (Sanh(m, id[j]) < 0) --j;
if (i <= j)
{
t = id[i]; id[i] = id[j]; id[j] = t;
++i; --j;
}
}
if (d < j) IdQSort(d, j);
if (i < c) IdQSort(i, c);
}
static int Sanh(int x, int y)
{
int ix = 0;
int iy = 0;
for (int i = 0; i < n; ++i)
{
ix = (x + i) % n;
iy = (y + i) % n;
if (s[ix] != s[iy])
return (s[ix] < s[iy]) ? -1 : 1;
}
return 0;
}
} // Strings
} // SangTao1
CHƢƠNG 5
PHƢƠNG PHÁP THAM LAM
File đính kèm:
Sáng tạo trong thuật toán và lập trình với ngôn ngữ Pascal và C# - Tập 1 - Chương 4_Tổ chức dữ liệu.pdf

