【挑战程序设计竞赛】后缀数组 实现字符串匹配

字符串后缀Suffix 指的是从字符串的某个位置开始到其末尾的字符串字串

后缀数组 Suffix Array 指的是将某个字符串的所有后缀按字典序排序之后得到的数组,不过数组中不直接保存所有

的后缀子串,只要记录相应的位置就好了。

下面的代码使用倍增法来构造后缀数组,该算法的复杂度是 O(n log n)常数因子比较大。

基于后缀数组的字符串匹配,我们可以通过二分搜索来完成,算法复杂度是 O(|T|log|S|) 其中 S 是主串,T是模式串

具体的后缀数组的原理可以详细去看看国家队集训的论文,罗穗骞《后缀数组——处理字符串的有力工具》:

 

#include <iostream>

#include <cstring>

#include <cstddef>

#include <cstdio>

#include <string>

#include <algorithm> 



using namespace std;

const int MAXN = 10001;

int n,k;

int rank[MAXN+1],tmp[MAXN+1];



bool comp_sa(int i, int j)

{

	if(rank[i] != rank[j]) 

		return rank[i] < rank[j];

	int ri = i+k <= n? rank[i+k] : -1;

	int rj = j+k <= n? rank[j+k] : -1;

	return ri < rj; 

}



void calc_sa(string &S, int *sa) //计算字符串S的后缀数组 

{

	n = S.size();

	

	//初始长度为1 

	for(int i = 0; i <= n; i++)

	{

		sa[i] = i;

		rank[i] = i < n ? S[i] : -1;

	}

	

	for( k =1; k <= n; k *= 2)

	{

		sort(sa,sa+n+1,comp_sa);

		

		//先在tmp中临时存储新计算的rank,再转存回rank中 

		tmp[sa[0]] = 0;

		for(int i = 1; i <= n; i++)

		{

			tmp[sa[i]] = tmp[sa[i-1]] + (comp_sa(sa[i-1],sa[i]) ? 1: 0);

		}

		for(int i = 0; i <= n; i++)

		{

			rank[i] = tmp[i];

		}

	}

}



void SuffixArrayMatch(string &S, int *sa, string T)

{

	calc_sa(S,sa);

	int lhs = 0, rhs = S.size();

	

	//使用二分查找 

	while(rhs - lhs > 1)

	{

		int mid = (lhs + rhs)>>1;

		int res = S.compare(sa[mid],T.size(),T); 

		if(res < 0)

			lhs = mid;

		else if(res == 0) 

		{

			cout<<"match at: "<<sa[mid]<<endl;

			lhs = mid;

		}

		else rhs = mid;

	}

}

int main()

{

	string S = "abracadabra";

	string T = "ab";

	int *sa = new int[S.size()+1];

 	SuffixArrayMatch(S,sa,T);

 	delete [] sa;

 	sa = NULL;

}


 

 

你可能感兴趣的:(程序设计)