[POI2000] 最长公共子串

  不知道为什么很多人学完SA后的第一道题都是这个。

  我寒假就写过这道题,而且SA只写了这道。

  但是,但是,但是,我今天又写了一遍,当然是A掉了。

  但是,我觉得手还比较生,准备在调整一下代码的格局。

  于是,我开始写第二遍。

  “好像不太对经啊。”

  “就是不对劲啊。”

  “怎么可能吗!”

  我对我的板子产生了质疑,于是来了组 aaaa 的数据测试一下。

  “。。。。。。”

  所以我是怎么A掉这题的......

  sa和rank都错了。

  不可思议。

  搞一下中间结果。

  “奥,我知道了。”

  在我原来的板子中,排名最小的后缀的排名是0,而对于长度不够没有第二关键字的后缀的第二关键字排名也是0。

  导致在第二次基数排序中(len=2),后缀Sufix(1)=aaa,后缀sufix(2)=aa的第一关键字与第二关键字均相同的情况。

  其中Sufix(1)的rank2由rank[1+2]=rank[3]=0得到,Sufix(2)的rank2由(i+l>=n)得到0,而显然这两个后缀是不同的的。

  在上午剩下的时间里我都在改板子了,最后算是改成正确并且满意的板子。

  细思极恐......

  回到这道题,对于SA的题,一般将最值问题转化为判定性问题。

  我们先将多个串接成一个串,中间用不同的字符连接,求一边SA。

  之后我们二分最大值x,并借助Height数组判定。

  首先,相同子串一定来自于一段连续的rank,也就是说要连续若干个Height值大于等于x。之后判断这连续若干个后缀中是否每个串的后缀都出现过。在拼串的时候记录一下每个位置的字符属于哪个串就可以了。

 

// q.c

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
const int M=2000*5+100;
struct Suffix_Array {
	int n,m,h[M];
	int sa[M],tmp[M],cnt[M];
	int rank[M],rank1[M],rank2[M];
	Suffix_Array():n(0),m(0) {
		mem(h); mem(sa); mem(tmp); mem(cnt); mem(rank); mem(rank1); mem(rank2);
	}
	bool same(int x,int y) {
		return rank1[sa[x]]==rank1[sa[y]]&&rank2[sa[x]]==rank2[sa[y]]?true:false;
	}
	void get_SA(char *s) {
		mem(cnt);
		for(int i=1;i<=n;i++) cnt[s[i]-'a']++;
		for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
		for(int i=n;i>=1;i--) rank[i]=cnt[s[i]-'a'];
		for(int l=1;l<=n;l<<=1) {
			for(int i=1;i<=n;i++) {
				rank1[i]=rank[i];
				rank2[i]=(i+l<=n)?rank[i+l]:0;
			}
			mem(cnt);
			for(int i=1;i<=n;i++) cnt[rank2[i]]++;
			for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
			for(int i=n;i>=1;i--) tmp[cnt[rank2[i]]--]=i;
			mem(cnt);
			for(int i=1;i<=n;i++) cnt[rank1[i]]++;
			for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
			for(int i=n;i>=1;i--) sa[cnt[rank1[tmp[i]]]--]=tmp[i];
			rank[sa[1]]=1;
			for(int i=2;i<=n;i++) 
				rank[sa[i]]=same(i,i-1)?rank[sa[i-1]]:rank[sa[i-1]]+1;
			if(rank[sa[n]]==n) break; 
		}
	}
	void get_HT(char *s) {
		for(int i=1,j=0,L=0;i<=n;i++) if(rank[i]>1) {
			j=sa[rank[i]-1];
			while(i+L<=n&&j+L<=n&&s[i+L]==s[j+L]) ++L;
			h[rank[i]]=L; if(L) --L;
		}
	}
	void prepare(char *s,int x,int y,int a[],int b[]) {
		n=x; m=y;
		get_SA(s); get_HT(s);
		memcpy(a,sa,sizeof(sa));
		memcpy(b,h,sizeof(h));
	}
}SA;
char s[M]; int n,len,pos[M],H[M],Sa[M]; bool vis[M];
bool check(int x) {
	for(int i=1,j=0,sum=0;i<=len;i=j+1,sum=0) {
		memset(vis,0,sizeof(vis));
		for(j=i;j<=len&&H[j]>=x;j++) 
			if(pos[Sa[j-1]]&&pos[Sa[j]]&&pos[Sa[j-1]]!=pos[Sa[j]]) {
				if(!vis[pos[Sa[j-1]]]) vis[pos[Sa[j-1]]]=true,++sum;
				if(!vis[pos[Sa[j]]]) vis[pos[Sa[j]]]=true,++sum;
				if(sum>=n) return true;
			}
	}
	return false;
}
int main() {
	freopen("pow.in","r",stdin);
	freopen("pow.out","w",stdout);
	int l=1,mid=0,r=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%s",s+l);
		r=strlen(s+1);
		for(int j=l;j<=r;j++) pos[j]=i;
		s[++r]='z'+i,l=++r;
	}
	SA.prepare(s,len=--r,30,Sa,H);
	for(l=0,mid=(l+r)>>1;l<=r;mid=(l+r)>>1) 
		check(mid)?l=mid+1:r=mid-1;
	printf("%d\n",l-1);
	return 0;
}

 

转载于:https://www.cnblogs.com/qjs12/p/8696799.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
2000年,POI数据下载是指在该年下载位置信息(POI)相关数据的行为。POI是指感兴趣点(Point of Interest),即地理位置上具有特殊意义或价值的地点。下载POI数据是为了获取这些特定地点的位置和相关信息,以便用于地图导航、社交媒体、市场研究等领域。 2000年,随着信息技术的快速发展,人们对位置信息的需求也日益增加。下载POI数据成为了一种常见的行为,以便更好地了解和利用周围环境。在这一年,下载POI数据的主要方式是通过互联网进行。 以2000年为例,要下载POI数据,首先需要访问相关的互联网平台或网站,如地图服务提供商、旅行网站或地理信息系统。然后,在相应的界面中,可以选择特定的地理区域、类型的POI,以及数据格式(如CSV、XML或JSON)等。接下来,点击下载按钮或链接,系统将开始传输POI数据到用户的设备上。 下载完成后,用户可以将这些POI数据导入到相应的应用程序或软件中进行进一步的分析和利用。例如,可以基于这些数据开发地图导航应用,帮助用户找到特定的商店、餐厅或景点。同时,还可以将POI数据与其他数据集结合,进行更深入的市场研究,比如确定最佳营销地点或了解消费者的行为模式。 总而言之,2000年的POI数据下载是一种通过互联网获取位置信息的行为,以满足人们对于地理位置和相关信息的需求。这一技术的发展为地图导航、社交媒体和市场研究等领域提供了更多可能性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值