现在的位置: 首页 > 综合 > 正文

poj 2774/poj 1743/poj 3261后缀数组应用

2018年02月23日 ⁄ 综合 ⁄ 共 5937字 ⁄ 字号 评论关闭

        最近看的后缀数组,做了几个应用。

        1、poj 2774 求两个字符串的最长公共子串。

         思路:将第二个串接到第一个串的后面,第一个串的结尾和第二个串的结尾分别用一个不会出现的字符标记,第二个串的结尾标记值要小。

         用大牛的模板求出sa,height,rank数组。然后去遍历数组求前面的和后面的最大公共前缀。判定的时候注意判定条件,我在判定的时候判定

         条件有漏洞,然后wa了好多次~~

         

#include <cstdio>
#include <cstdlib>
#include <climits>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
#define mid(x,y) (x+y)>>1
#define INF (INT_MAX/10)
#define SQR(x) x*x
#define rep(i, n) for(int i=0;i<n;i++)
#define repf(i, a, b) for(int i=a;i<=b;i++)
#define repd(i, a, b) for(int i=a;i>=b;i--)
#define clr(ar, val) memset(ar, val, sizeof(ar))
#define N 1000000
int sa[N+10],c[N+10],wa[N+10],wb[N+10];
int cmp(int *y,int a,int b,int l){
	return y[a]==y[b]&&y[a+l]==y[b+l];
}
void da(string r,int *sa){
	int i,j,p,*rand=wa,*y=wb,m=255,n=r.length();
	for(i=0;i<=m;i++) c[i]=0;
	for(i=0;i<n;i++) c[rand[i]=r[i]]++;
	for(i=0;i<m;i++) c[i+1]+=c[i];
	for(i=n-1;i>=0;i--) sa[--c[rand[i]]]=i;
	for(j=1,p=1;p<n;j*=2,m=p){
		for(i=n-j,p=0;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
		for(i=0;i<=m;i++) c[i]=0;
		for(i=0;i<n;i++) c[rand[y[i]]]++;
		for(i=0;i<m;i++) c[i+1]+=c[i];
		for(i=n-1;i>=0;i--) sa[--c[rand[y[i]]]]=y[i];
		swap(y,rand);
		for(i=1,p=1,rand[sa[0]]=0;i<n;i++){
			rand[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
		}
	}
}
void height(string r,int *sa,int *h){
	int i,j,*rank=wa,k=0,n=r.length();
	for(i=0;i<n;i++) rank[sa[i]]=i;
	for(i=0;i<n-1;h[rank[i++]]=k)
		for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
string str1,str2;
int ans[N+10],h[N+10];
int main(){
	int k,maxs;
	while(cin>>str1>>str2){
		clr(ans,0);clr(h,0);
		str1=str1+'$'+str2+'#';
		//cout << str1 << endl;
		k=str1.find('$');
		//cout << k << ' ' << str1.length() << endl;
		da(str1,ans);
		//rep(i,10) cout << ans[i] << ' ' ;
		//cout << endl;
		height(str1,ans,h);
		//rep(i,10) cout << h[i] << ' ' ;
		//cout << endl;
		maxs=0;
		repf(i,1,(int)str1.length()-1) 
		    if((ans[i]-k)*(ans[i-1]-k+h[i])<0||(ans[i]-k+h[i])*(ans[i-1]-k)<0)
		maxs=max(maxs,h[i]);
		cout << maxs << endl;
	}
    return 0;
}

        2、poj 1743  求一个字符串中重复出现的最长的子串(不可重叠)

        思路:数据要先预处理成一组新的字符串,即后面的减掉前面的。对新串求sa,height,rank数组,然后二分的去搜可能的值,我当时没有明白二分的意义,

        后来看了别人的代码,再对着文字的理论结合大概理解了。找一个可能的长度,带到字符串中,判定是否可行,可行的话看是否有更长的,不可行看是不

        是有更短的。这样二分的找出结果。

         

#include <cstdio>
#include <cstdlib>
#include <climits>
#include <cmath>
#include <cstring>

#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
#define Mid(x,y) (x+y)>>1
#define INF (INT_MAX/10)
#define SQR(x) x*x
#define rep(i, n) for(int i=0;i<n;i++)
#define repf(i, a, b) for(int i=a;i<=b;i++)
#define repd(i, a, b) for(int i=a;i>=b;i--)
#define clr(ar, val) memset(ar, val, sizeof(ar))
#define N 1000000
int sa[N+10],c[N+10],wa[N+10],wb[N+10];
bool cmp(int *y,int a,int b,int l){
	return y[a]==y[b]&&y[a+l]==y[b+l];
}
template <class T>
void suffix(T r,int n){
	int i,j,p,*rank=wa,*y=wb,m=255;
	for(i=0;i<m;i++) c[i]=0;
	for(i=0;i<n;i++) c[rank[i]=r[i]]++;
	for(i=0;i<m;i++) c[i+1]+=c[i];
	for(i=n-1;i>=0;i--) sa[--c[rank[i]]]=i;

	for(j=1,p=1;p<n;j*=2,m=p){
		for(p=0,i=n-j;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;

		for(i=0;i<m;i++) c[i]=0;
		for(i=0;i<n;i++) c[rank[y[i]]]++;
		for(i=0;i<m;i++) c[i+1]+=c[i];
		for(i=n-1;i>=0;i--) sa[--c[rank[y[i]]]]=y[i];

		swap(rank,y);
		for(i=1,p=1,rank[sa[0]]=0;i<n;i++)
			rank[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
	}
}
int h[N+10];
template <class T>
void height(T r,int n){
	int i,j,k=0,*rank=wa;
	for(i=0;i<n;i++) rank[sa[i]]=i;
	for(i=0;i<n-1;h[rank[i++]]=k)
		for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
int check(int mid,int len){
	for(int i=2;i<len;i++){
		if(h[i]<mid) continue;
		for(int j=i-1;j>=2;j--){
			if(abs(sa[i]-sa[j])>mid) return 1;
			if(h[j]<mid) break;
		}
	}
	return 0;
}
int str[N+10];
int main(){
	int n,ans;
	while(1){
		cin >> n;
		if(!n) break;
		rep(i,n) {
			scanf("%d",str+i);
			if(i) str[i-1]=str[i]-str[i-1]+100;
		}
		if(n<10){
			cout << 0 << endl;
			continue;
		}
		str[n-1]=0;
	//	rep(i,n) cout << str[i] << ' ' ;
	//	cout << endl;
		suffix(str,n);
		height(str,n);
	//	rep(i, n) cout << sa[i] << ' ' ;
	//	cout << endl;
	//	rep(i, n) cout << h[i] << ' ' ;
	//	cout << endl;
		int mid,left,right;
		left=3,right=n;
	   	ans=0;
		while(right>=left){
			mid=Mid(left,right);
//			cout << mid << endl;
			if(check(mid,n)){
				left=mid+1;
				ans=mid;
			}
			else right=mid-1;
		}
	//	cout << ans << endl;
		if(ans<4)
			cout << 0 << endl;
		else 
			cout << ans+1 << endl;
	}
    return 0;
}

        3、poj 3261 同样的求最长的重复子串

        题意很明确,求一个最长的出现K 次的重复子串,可以重叠。同样处理出sa,height,rank数组,再二分的搜索可能的长度。这个题我wa了几次,

        wa的有点坑,一个等号的问题(<=len)!!!!eggache~~

       

#include <cstdio>
#include <cstdlib>
#include <climits>
#include <cmath>
#include <cstring>

#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
#define Mid(x,y) (x+y)>>1
#define INF (INT_MAX/10)
#define SQR(x) x*x
#define rep(i, n) for(int i=0;i<n;i++)
#define repf(i, a, b) for(int i=a;i<=b;i++)
#define repd(i, a, b) for(int i=a;i>=b;i--)
#define clr(ar, val) memset(ar, val, sizeof(ar))
#define N 1000000
int sa[N+10],c[N+10],wa[N+10],wb[N+10];
bool cmp(int *y,int a,int b,int l){
	return y[a]==y[b]&&y[a+l]==y[b+l];
}
template <class T>
void suffix(T r,int n,int m){
	int i,j,p,*rank=wa,*y=wb;
	for(i=0;i<m;i++) c[i]=0;
	for(i=0;i<n;i++) c[rank[i]=r[i]]++;
	for(i=0;i<m;i++) c[i+1]+=c[i];
	for(i=n-1;i>=0;i--) sa[--c[rank[i]]]=i;

	for(j=1,p=1;p<n;j*=2,m=p){
		for(p=0,i=n-j;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;

		for(i=0;i<m;i++) c[i]=0;
		for(i=0;i<n;i++) c[rank[y[i]]]++;
		for(i=0;i<m;i++) c[i+1]+=c[i];
		for(i=n-1;i>=0;i--) sa[--c[rank[y[i]]]]=y[i];

		swap(rank,y);
		for(i=1,p=1,rank[sa[0]]=0;i<n;i++)
			rank[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
	}
}
int h[N+10];
template <class T>
void height(T r,int n){
	int i,j,k=0,*rank=wa;
	for(i=0;i<n;i++) rank[sa[i]]=i;
	for(i=0;i<n-1;h[rank[i++]]=k)
		for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
int check(int mid,int len,int k){
	int i,j=1;
	for(i=2;i<=len;i+=j){
//		cout << mid << ' '  << k <<  endl;
		if(h[i]<mid) continue;
		int flag=0;
		while(i<=len&&h[i]>=mid)
			flag++,i++;
		if(flag>=k) return 1;
	}
	return 0;
}
int str[N+10];
int main(){
	int n,k;
	while(cin>>n>>k){
		rep(i,n) {
			scanf("%d",str+i);
			str[i]++;
		}
		str[n]=0;
		suffix(str,n+1,N+3);
		height(str,n+1);
//		rep(i, n) cout << h[i] << ' ' ;
//		cout << endl;
		int ans=0,mid,left,right;
		left=0,right=n;//=*max_element(h+2,h+n);
//		cout << "fuck" <<  right << endl;
		while(right>=left){
			mid=Mid(left,right);
//			cout << "fuck mid " << mid << endl;
			if(check(mid,n,k-1))
				left=mid+1,ans=mid;
			else right=mid-1;
		}
		cout << ans << endl;
	}
    return 0;
}	

【上篇】
【下篇】

抱歉!评论已关闭.