HDU 2328 Corporate Identity 後綴數組

求多個字符串的最長公共子串,若有多個輸出字典序最小。


先複習一下,(i,j)表示排名i、j的串的最長公共前綴。(i,j) = min[(i,i+1),(i+1,i+2),......,(j-1,j)]。

兩個字符串的最長公共前綴求法:

將兩個字符串合起來,中間加個特殊符號,然後對整個字符串求後綴數組。

掃描height數組,如果排名i和i+1的串分別屬於不同的原始串,則用height[i+1]更新結果。


那多個串的處理方法也類似:

把所有串合併起來,兩兩之間加特殊符號然後搞下後綴數組。

現在就是要找一個區間,要求每個原始字符串都至少要有一個子串在此區間內,那麼這個區間內串的公共前綴必然是所有原始串的公共子串,於是就可以用這個區間內的最小height值更新結果。

這種區間如果有互相包含的話,外面那個肯定不用考慮,所以用se標記從左到右掃一遍就好,區間最小值用線段樹求一下即可。


#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define SIZE 1000005 
int N;
char S[SIZE],S1[SIZE]; // SIZE > 256
int sa[SIZE], height[SIZE], rank[SIZE], tmp[SIZE], top[SIZE];
//rank[i] i是第幾名 
//sa[i] 第i名是什麼
//height[i] 第i名和第i-1名的最長公共前綴長度
//名次:1~N  序號:0~N-1
struct Unit{
	int v,id;
	bool friend operator<(Unit a,Unit b){
		return a.v<b.v;
	}
};
Unit sunit[SIZE];
void makesa(){ // O(SIZE * log SIZE)
	int i, j, n, len, na;
	n=N+1;
	S[n-1]=0;
	na = (n < 256 ? 256 : n);
	memset(top, 0, na * sizeof(int));
	/*-------char------*/
	for (i = 0; i < n ; i++) top[ rank[i] = S[i] & 0xff ]++;
	for (i = 1; i < na; i++) top[i] += top[i - 1];
	for (i = 0; i < n ; i++) sa[ --top[ rank[i] ] ] = i;
	/*---------char----*/
	/*-------int-------
	for(i=0;i<n;i++){
		sunit[i].id=i;
		sunit[i].v=S[i];
	}
	sort(sunit,sunit+n);
	int t=0;
	for(i=0;i<n;i++){
		sa[i]=sunit[i].id;
		if(i&&sunit[i].v!=sunit[i-1].v){
			t++;top[t+1]+=top[t];
		}
		rank[sunit[i].id]=t;top[t+1]++;
	}
	for(t++;t<n;t++)top[t]=top[t-1];
	//-----------------*/
	for (len = 1; len < n; len <<= 1) {
		for (i = 0; i < n; i++) {
			j = sa[i] - len; if (j < 0) j += n;
			tmp[ top[ rank[j] ]++ ] = j;
		}
		sa[ tmp[ top[0] = 0 ] ] = j = 0;
		for (i = 1; i < n; i++) {
			if (rank[ tmp[i] ] != rank[ tmp[i-1] ] ||
				rank[ tmp[i]+len ]!=rank[ tmp[i-1]+len ])
				top[++j] = i;
			sa[ tmp[i] ] = j;
		}
		memcpy(rank, sa , n * sizeof(int));
		memcpy(sa , tmp, n * sizeof(int));
		if (j >= n - 1) break;
	}
}
void lcp(){ // O(4 * SIZE)
	int i, j, k,n=N+1;
	for (j = rank[height[i=k=0]=0]; i < n - 1; i++, k++)
		while (k >= 0 && S[i] != S[ sa[j-1] + k ])
			height[j] = (k--), j = rank[ sa[j] + 1 ];
}
int f_min(int x,int y){
	return x<y?x:y;
}
int loc[1000000],num;
void get_data(){
	S[0]=0;
	char str[205];
	int i,j=0;
	for(i=0;i<num;i++){
		scanf("%s",str);
		if(!i){
			strcat(S,str);
			for(;S[j];j++)loc[j]=i;
		}else{
			strcat(S,"*");
			loc[j++]=-1;
			strcat(S,str);
			for(;S[j];j++)loc[j]=i;
		}
	}
	N=j;
	makesa();
	lcp();
}
int mark[4000];
char best[10000],tbest[10000];
int len;
struct Node{
	int s,e,v;
};
Node node[2100000];
int get_min(int loc,int s,int e){
	if(node[loc].s==s&&node[loc].e==e)return node[loc].v;
	int mid=(node[loc].s+node[loc].e)>>1;
	if(e<=mid)return get_min(loc<<1,s,e);
	else if(s>mid)return get_min(loc<<1|1,s,e);
	else return f_min(get_min(loc<<1,s,mid),get_min(loc<<1|1,mid+1,e));
}
void build_tree(int loc,int s,int e){
	node[loc].s=s;node[loc].e=e;
	if(s==e){
		node[loc].v=height[s];
		return;
	}
	int mid=(s+e)>>1;
	build_tree(loc<<1,s,mid);
	build_tree(loc<<1|1,mid+1,e);
	node[loc].v=f_min(node[loc<<1].v,node[loc<<1|1].v);
}
// aca*ca*cac
// 0123456789
void disp(){
	int i;
	for(i=0;i<=N;i++)printf("%d ",sa[i]);printf("\n");
	for(i=0;i<=N;i++)printf("%d ",loc[i]);printf("\n");
	for(i=0;i<=N;i++)printf("%d ",rank[i]);printf("\n");
	for(i=0;i<=N;i++)printf("%d ",height[i]);printf("\n");
}
void run(){
	if(num==1){
		printf("%s\n",S);
		return;
	}
	build_tree(1,1,N);
	int s,e,cnt=0,tlen,i;
//	disp();
	s=e=1;
	memset(mark,0,sizeof(mark));
	best[0]=0;len=0;
	while(true){
		while(e<=N&&cnt<num){
			if(loc[sa[e]]!=-1&&!mark[loc[sa[e]]])cnt++;
			mark[loc[sa[e]]]++;
			e++;
		}
		if(cnt<num)break;
		while(true){
			if(loc[sa[s]]==-1)s++;
			else if(mark[loc[sa[s]]]>1){
				mark[loc[sa[s]]]--;
				s++;
			}else break;
		}
//		printf("%d %d s e ",s,e);
		tlen=get_min(1,s+1,e-1);
	//	printf("%d\n",tlen);
	//	for(i=sa[s];i<tlen;i++)if(S[i]=='*')break;
	//	tlen=i;
		copy(S+sa[s],S+sa[s]+tlen,tbest);
		tbest[tlen]=0;//printf("%s * %s %d\n",tbest,best,strcmp(tbest,best));
		if(tlen>len||tlen==len&&strcmp(tbest,best)<0){
			len=tlen;
			strcpy(best,tbest);
		}
		mark[loc[sa[s]]]--;cnt--;
		s++;
	}
	if(!len)printf("IDENTITY LOST\n");
	else printf("%s\n",best);
}
/*
4
asidugqw
grweiudbq
fwugcas
wsfikuwgerf
*/
int main(){
	while(scanf("%d",&num),num){
		get_data();
		run();
	}
	return 0;
}



發佈了71 篇原創文章 · 獲贊 4 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章