首页 > 其他分享 >Beautiful Words(2022 BSPC B)

Beautiful Words(2022 BSPC B)

时间:2022-10-07 11:23:37浏览次数:72  
标签:Beautiful lcp int SIGMA BSPC 2022 SA type sa

传送门

题目大意

你有一个长度为\(N\)的字符串\(A\)和一个字符串集\(S\),问所有\(A\)的循环同构中与\(S\)中的字符串的最长公共子串长度的最大值最小是多少。\((1\le N\le10^5,\sum|s|\le10^6)\)

思路

我们可以首先把\(A\)串中从所有位置开始的与\(S\)集合中的串的最长公共子串的长度都求出来,这个是利用后缀数组\(O(N)\)就可以解决的,然后对于长度为\(N\)的每一段,都可以分为三部分,左边的,全部在里面的和右边的,里面的和右边的都可以用优先队列存一下,里面的长度优先,右边的左端点优先,当右边的不满足条件时,他会到里面,当里面的不满足条件时,他会到左边,然后因为左边只是一直递减,所以只需要存一个长度即可,最后一段的值便是三个值取个\(max\),最后答案与所有段的值取个\(min\)即可,由于最多只会有\(N\)个最长公共子串的长度,所以这样复杂度是可以保证的,然后由于优先队列自带一个\(log\),所以最后的复杂度是\(O(N\log)\)。优先队列的常数是真的大,哭。

代码

const int MAXLEN = 1000005;
struct SAIS
{
	// 后缀类型
#define L_TYPE 0
#define S_TYPE 1
	
	int st[MAXLEN];
	int rl[MAXLEN],rk[MAXLEN],lcp[MAXLEN];
	int n;
	//rl:后缀排序后的排名列表,rl[i]代表第i名的后缀的序号
	//rk:名次,rk[i]代表后缀i的名次
	//lcp:height数组,lcp[i]代表列表中第i个后缀和第i-1个后缀的lcp长度
	
	void init(const string&s,const int&_n)
	{
		n = _n;
		for (int i=1;i<=n;++i) st[i] = s[i-1];
		// st中的字符必须调成正数!!!!!!!!!!!!!!
	}
	
	// 判断一个字符是否为LMS字符
	inline bool is_lms_char(int *type, int x) {
		return x > 1 && type[x] == S_TYPE && type[x - 1] == L_TYPE;
	}
	
	// 判断两个LMS子串是否相同
	inline bool equal_substring(int *S, int x, int y, int *type) {
		do {
			if (S[x] != S[y])
				return false;
			x++, y++;
		} while (!is_lms_char(type, x) && !is_lms_char(type, y));
		
		return S[x] == S[y] && type[x] == type[y];
	}
	
	// 诱导排序(从*型诱导到L型、从L型诱导到S型)
	// 调用之前应将*型按要求放入SA中
	inline void induced_sort(int *S, int *SA, int *type, int *bucket, int *lbucket,
							 int *sbucket, int nn, int SIGMA) {
		for (int i = 1; i <= nn; i++)
			if (SA[i] > 1 && type[SA[i] - 1] == L_TYPE)
				SA[lbucket[S[SA[i] - 1]]++] = SA[i] - 1;
		for (int i = 0; i <= SIGMA; i++)  // Reset S-type bucket
			sbucket[i] = bucket[i];
		for (int i = nn; i >= 1; i--)
			if (SA[i] > 1 && type[SA[i] - 1] == S_TYPE)
				SA[sbucket[S[SA[i] - 1]]--] = SA[i] - 1;
	}
	
	// SA-IS主体
	// S是输入字符串,length是字符串的长度, SIGMA是字符集的大小
	int *sais(int *S, int length, int SIGMA) {
		int nn = length;
		assert(S[nn]==0);
		int *type = new int[nn + 5];  // 后缀类型
		int *position = new int[nn + 5];  // 记录LMS子串的起始位置
		int *name = new int[nn + 5];  // 记录每个LMS子串的新名称
		int *SA = new int[nn + 5];  // SA数组
		int *bucket = new int[SIGMA + 5];  // 每个字符的桶
		int *lbucket = new int[SIGMA + 5];  // 每个字符的L型桶的起始位置
		int *sbucket = new int[SIGMA + 5];  // 每个字符的S型桶的起始位置
		
		// 初始化每个桶
		memset(bucket, 0, sizeof(int) * (SIGMA + 5));
		for (int i = 1; i <= nn; i++)
			bucket[S[i]]++;
		for (int i = 0; i <= SIGMA; i++) {
			if (i==0)
			{
				bucket[i] = bucket[i];
				lbucket[i] = 1;
			}else
			{
				bucket[i] += bucket[i - 1];
				lbucket[i] = bucket[i - 1] + 1;
			}
			sbucket[i] = bucket[i];
		}
		
		// 确定后缀类型(利用引理2.1)
		type[nn] = S_TYPE;
		for (int i = nn - 1; i >= 1; i--) {
			if (S[i] < S[i + 1])
				type[i] = S_TYPE;
			else if (S[i] > S[i + 1])
				type[i] = L_TYPE;
			else
				type[i] = type[i + 1];
		}
		// 寻找每个LMS子串
		int cnt = 0;
		for (int i = 1; i <= nn; i++)
			if (is_lms_char(type,i))
				position[++cnt] = i;
		// 对LMS子串进行排序
		fill(SA, SA + nn + 3, -1);
		for (int i = 1; i <= cnt; i++)
			SA[sbucket[S[position[i]]]--] = position[i];
		induced_sort(S, SA, type, bucket, lbucket, sbucket, nn, SIGMA);
		
		// 为每个LMS子串命名
		fill(name, name + nn + 3, -1);
		int lastx = -1, namecnt = 1;  // 上一次处理的LMS子串与名称的计数
		bool flag = false;  // 这里顺便记录是否有重复的字符
		for (int i = 2; i <= nn; i++) {
			int x = SA[i];
			
			if (is_lms_char(type, x)) {
				if (lastx >= 0 && !equal_substring(S, x, lastx, type))
					namecnt++;
				// 因为只有相同的LMS子串才会有同样的名称
				if (lastx >= 0 && namecnt == name[lastx])
					flag = true;
				
				name[x] = namecnt;
				lastx = x;
			}
		}  // for
		name[nn] = 0;
		
		// 生成S1
		int *S1 = new int[cnt+5];
		int pos = 0;
		for (int i = 1; i <= nn; i++)
			if (name[i] >= 0)
				S1[++pos] = name[i];
		int *SA1;
		if (!flag) {
			// 直接计算SA1
			SA1 = new int[cnt + 5];
			
			for (int i = 1; i <= cnt; i++)
				SA1[S1[i]+1] = i;
		} else
			SA1 = sais(S1, cnt, namecnt);  // 递归计算SA1
		
		// 从SA1诱导到SA
		for (int i = 0; i <= SIGMA; i++) {
			if (i==0)
				lbucket[i] = 1;
			else
				lbucket[i] = bucket[i - 1] + 1;
			sbucket[i] = bucket[i];
		}
		fill(SA, SA + nn + 3, -1);
		for (int i = cnt; i >= 1; i--)  // 这里是逆序扫描SA1,因为SA中S型桶是倒序的
			SA[sbucket[S[position[SA1[i]]]]--] = position[SA1[i]];
		induced_sort(S, SA, type, bucket, lbucket, sbucket, nn, SIGMA);
		
		delete[] S1;
		delete[] SA1;
		delete[] bucket;
		delete[] lbucket;
		delete[] sbucket;
		delete[] position;
		delete[] type;
		delete[] name;
		// 后缀数组计算完毕
		return SA;
	}
	
	void build()
	{
		st[0]=st[n+2]=-1;
		st[n+1]=0;
		int SIGMA = 0;
		for (int i=1;i<=n;++i) SIGMA = max(SIGMA,st[i]);
		int * sa = sais(st,n+1,SIGMA);
		for (int i=2;i<=n+1;++i) rk[sa[i]]=i-1;
		delete[] sa;
		for (int i=1;i<=n;++i) rl[rk[i]]=i;
		for (int i=1,len=0;i<=n;++i)
		{
			if (len) --len;
			while (i+len<=n && rl[rk[i]-1]+len<=n && st[i+len]==st[rl[rk[i]-1]+len]) ++len;
			lcp[rk[i]]=len;
		}
	}

#undef L_TYPE
#undef R_TYPE
}sa;
string S,s[10005];
vector<int> a[200005];
struct node
{
	int left,len;
	bool operator<(const node&n)const
	{
		return len<n.len;
	}
};
struct nodee
{
	int left,len;
	bool operator<(const nodee&n)const
	{
		return left>n.left;
	}
};

void solve()
{
	int n,m;
	cin>>n>>m;
	cin>>S;
	for(int i=1;i<=n;i++)sa.st[++sa.n]=S[i-1]+m;
	for(int i=1;i<=n;i++)sa.st[++sa.n]=S[i-1]+m;
	for(int i=1;i<=m;i++)
	{
		sa.st[++sa.n]=i;
		cin>>s[i];
		for(auto it:s[i])sa.st[++sa.n]=it+m;
	}
	sa.build();
	int lcp=inf;
	for(int i=1;i<=sa.n;i++)
	{
		if(sa.rl[i]>2*n)lcp=inf;
		else
		{
			lcp=min(lcp,sa.lcp[i]);
			if(lcp)a[sa.rl[i]].push_back(lcp);
		}
	}
	lcp=0;
	for(int i=sa.n;i>=1;i--)
	{
		if(sa.rl[i]>2*n)lcp=sa.lcp[i];
		else
		{
			if(lcp)a[sa.rl[i]].push_back(lcp);
			lcp=min(lcp,sa.lcp[i]);
		}
	}
	int l=1,r=n;
	priority_queue<node>q;
	priority_queue<nodee>rig;
	int left_max_len=0;
	int ans=inf;
	for(int i=1;i<n;i++)
	{
		for(auto it:a[i])
		{
			if(i+it-1<=n)q.push({i,it});
			else rig.push({i,it});
		}
	}
	for(;r<=2*n;l++,r++)
	{
		left_max_len=max(0,left_max_len-1);
		for(auto it:a[r])
		{
			if(r+it-1<=r)q.push({r,it});
			else rig.push({r,it});
		}
		while(!rig.empty()&&rig.top().left+rig.top().len-1<=r)
		{
			q.push({rig.top().left,rig.top().len});
			rig.pop();
		}
		while(!q.empty()&&q.top().left<l)
		{
			left_max_len=max(left_max_len,q.top().len-(l-q.top().left+1));
			q.pop();
		}
		int maxx=left_max_len;
		if(!q.empty())maxx=max(maxx,q.top().len);
		if(!rig.empty())maxx=max(maxx,r-rig.top().left+1);
		ans=min(ans,maxx);
	}
	cout<<ans<<'\n';
}

标签:Beautiful,lcp,int,SIGMA,BSPC,2022,SA,type,sa
From: https://www.cnblogs.com/Jerry-Black/p/16759298.html

相关文章