串的模式匹配算法(求子串位置的定位函數Index(S,T,pos))


串的模式匹配的一般方法如算法4.5(在bo4-1.cpp 中)所示:由主串S 的第pos 個字
符起,檢驗是否存在子串T。首先令i 等於
pos(i 爲S 中當前待比較字符的位序),j 等於
1(j 爲T 中當前待比較字符的位序),如果S 的
第i 個字符與T 的第j 個字符相同,則i、j 各
加1 繼續比較,直至T 的最後一個字符(找
到)。如果還沒到T 的最後一個字符,比較就
出現了不同(沒找到),則令i 等於pos+1,j 等
於1,由pos 的下一個位置起,繼續查找是否
存在子串T。這個過程如圖410 所示。

在算法4.5 中,主串S 的指針i 總要回溯,特別是在如圖410 所示的有較多字符匹
配而又不完全匹配的情況下,回溯得更多。這時,主串S 的一個字符要進行多次比較,顯
然效率較低。
如果能使主串S 的指針i 不回溯,在有些情況下效率則會大爲提高。這是可以做到
的,因爲主串S 中位於i-1,i-2,⋯ 的字符恰和子串T 中位於j-1,j-2,⋯ 的字符相
等,如圖410 所示。仍以圖410 爲例,當S 和T 在第i(終值)個字符處字符不相符
時,i 仍保持在終值處不動,j 回溯到第1 個字符與i 的當前字符繼續進行比較。j 回溯到第
幾個字符是由子串T 的模式決定的。算法4.7 根據子串T 生成的next 數組指示j 回溯到第
幾個字符。next 數組的意義是這樣的:如果next[j]=k,當子串T 的第j 個字符與主串S 的
第i 個字符“失配”時,S 的第i 個字符繼續與T 的第k 個字符進行比較,T 的第k 個字符
之前的那些字符均與S 的第i 個字符之前的字符匹配。以教科書中圖4.5 爲例,設子串T
爲“abaabcac”。當T 的第5 個字符與S 的第i 個字符失配時,S 的第i-1 個字符一定是a,
和T 的第4 個字符相等。它和T 的第1 個字
符相等。這樣,S 的第i 個字符和T 的第2 個
字符開始比較即可。所以, 對於模式串
“abaabcac”,next[5]=2,詳見圖411。
算法4.7 求子串的數組next[]還有可改
進之處。以圖411 爲例:如果T 的第5 個
字符與S 的第i 個字符失配,則S 的第i 個字
符一定不是b。這樣,儘管S 的第i-1 個字符

是a,和T 的第1 個字符相等,但S 的第i 個字符肯定和T 的第2 個字符b 不相等。所以
可令next[5]=1,使S 的第i 個字符和T 的第1 個字符開始比較。這樣使得模式串又向右
移了一位,提高了匹配的效率。算法4.8 是改進的求數組next[](在算法4.8 中的形參是
nextval[])的算法。
算法4.6 是改進的模式匹配算法。它利用算法4.7 或算法4.8 求得的數組next[],提
高了算法的效率。algo4-1.cpp 是實現改進的模式匹配算法的程序。函數get_next()和
get_nextval()分別求得給定的模式串的數組next[]和nextval[],函數Index_KMP()利用數
組next[]或nextval[]求出模式串在主串中的位置。其中,next[j]=0,並不是將主串的當前
字符與模式串的第0 個字符進行比較(模式串也沒有第0 個字符),而是主串當前字符的下
一個字符與模式串的第1 個字符進行比較。


// algo4-1.cpp 實現算法4.6、4.7、4.8的程序
#include"c1.h"
#include"c4-1.h"
#include"bo4-1.cpp"
void get_next(SString T,int next[])
{ // 求模式串T的next函數值並存入數組next。算法4.7
	int i=1,j=0;
	next[1]=0;
	while(i<T[0])
		if(j==0||T[i]==T[j])
		{
			++i;
			++j;
			next[i]=j;
		}
		else
			j=next[j];
}
void get_nextval(SString T,int nextval[])
{ // 求模式串T的next函數修正值並存入數組nextval。算法4.8
	int i=1,j=0;
	nextval[1]=0;
	while(i<T[0])
		if(j==0||T[i]==T[j])
		{
			++i;
			++j;
			if(T[i]!=T[j])
				nextval[i]=j;
			else
				nextval[i]=nextval[j];
		}
		else
			j=nextval[j];
}
int Index_KMP(SString S,SString T,int pos,int next[])
{ // 利用模式串T的next函數求T在主串S中第pos個字符之後的位置的KMP算法。
	// 其中,T非空,1≤pos≤StrLength(S)。算法4.6
	int i=pos,j=1;
	while(i<=S[0]&&j<=T[0])
		if(j==0||S[i]==T[j]) // 繼續比較後繼字符
		{
			++i;
			++j;
		}
		else // 模式串向右移動
			j=next[j];
		if(j>T[0]) // 匹配成功
			return i-T[0];
		else
			return 0;
}
void main()
{
	int i,*p;
	SString s1,s2; // 以教科書算法4.8之上的數據爲例
	StrAssign(s1,"aaabaaaab");
	printf("主串爲");
	StrPrint(s1);
	StrAssign(s2,"aaaab");
	printf("子串爲");
	StrPrint(s2);
	p=(int*)malloc((StrLength(s2)+1)*sizeof(int)); // 生成s2的next數組空間
	get_next(s2,p); // 利用算法4.7,求得next數組,存於p中
	printf("子串的next數組爲");
	for(i=1;i<=StrLength(s2);i++)
		printf("%d ",*(p+i));
	printf("\n");
	i=Index_KMP(s1,s2,1,p); // 利用算法4.6求得串s2在s1中首次匹配的位置i
	if(i)
		printf("主串和子串在第%d個字符處首次匹配\n",i);
	else
		printf("主串和子串匹配不成功\n");
	get_nextval(s2,p); // 利用算法4.8,求得next數組,存於p中
	printf("子串的nextval數組爲");
	for(i=1;i<=StrLength(s2);i++)
		printf("%d ",*(p+i));
	printf("\n");
	printf("主串和子串在第%d個字符處首次匹配\n",Index_KMP(s1,s2,1,p));
}

代碼的運行結果如下:

/*
主串爲aaabaaaab
子串爲aaaab
子串的next數組爲0 1 2 3 4
主串和子串在第5個字符處首次匹配
子串的nextval數組爲0 0 0 0 4
主串和子串在第5個字符處首次匹配
Press any key to continue
*/



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章