题解 HDU6223 Infinite Fraction Path

Posted point-king

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 HDU6223 Infinite Fraction Path相关的知识,希望对你有一定的参考价值。

这是一个作者历经千辛万苦,从无数次 (WA)(RE)(TLE) 中得到的心得体会与感悟。

这是一道后缀排序的进阶题,是一道很好的题目,作者认为它可以很好的加深我们对于后缀排序的理解。

首先,我们可以很容易的判断出来,这是一道后缀数组的题目,因为他要求我们找出在一棵基环树上,字典序最大的一个后缀。

但是,他与普通的后缀数组又有着不同的地方,具体体现在如下:

  1. 由于是在基环树上的后缀,所以长度是无限的(因为有环),题目要求我们找出字典序最大的长度为 (n) 的后缀,所以在处理 (tp) 数组的时候,我们需要特殊处理。

  2. 同样还是 (tp) 数组的处理,因为我们是在树上找第二关键词,也就是上一次排序的结果,我们不能直接通过上一次的 (sa) 数组的加减来获得 (tp) 数组的位置。

针对第一条,我们可以轻易得到,如果一个长度大于 (n) 的后缀的字典序是最大的,那么它长度为 (n) 的前缀的字典序也一定是最大的,所以我们可以直接倍增。同时对于此时 (tp) 数组的处理我们就可以不用考虑长度的限制(因为原本的 (tp) 数组如果在长度不够时,是直接变为最前的位置的)。

针对第二条,我们可以有多种方法来解决这个问题。

方法一:我们可以运用倍增,找到当前点往后 (2^k) 步后所在点的位置,而当前点的 (tp) 数组就是由这个点的 (sa) 数组所更新的,我们可以考虑用一个 (vector) 或者是 (queue) (反正可以存储数据都可以)在往后 (2^k) 步的点的下标位来存下当前点,最后在遍历一遍 (sa) 数组,同时调用出 (queue) 中存储的点,来更新 (tp) 数组。代码如下:

	int tmp=0;
	for(int i=1;i<=n;++i)
	q[to[i][k]].push(i);
	for(int i=1;i<=n;++i)
	{
		while(q[sa[i]].size())
		{
			tp[++tmp]=q[sa[i]].front();
			q[sa[i]].pop();
		}
	}

方法二:我们可以回忆一下运用在后缀数组中的基数排序写法:是先运用一个桶数组,搞一遍前缀和来得出在第一关键词 (rk) 下每一种关键词的排名区间,在倒着遍历 (tp) 数组,就可以得到 (sa) 数组了。我们知道, (tp) 数组和 (sa) 数组的意义是完全一样的,即 (tp_i) 表示,排名为 (i) 的后缀的编号。所以我们完全可以利用 (rk) 数组重新计算一遍当前点往后 (2^k) 步后的 (tp) 数组,具体操作十分类似于我们第一次进行的基数排序,只不过加入点的 (rk) 时要加上倍增数组。代码如下:

	memset(tax,0,sizeof(tax));
	for(int i=1;i<=n;++i)
	tax[rk[to[i][k]]]++;
	for(int i=1;i<=m;++i)
	tax[i]+=tax[i-1];
	for(int i=1;i<=n;++i)
	tp[tax[rk[to[i][k]]]--]=i;

两种方法都是正确的,但是方法二的时间复杂度好像更优秀一点(原因是方法一我 (T) 飞了???)。而剩下的部分,就是利用得到的后缀数组直接模拟 (n) 遍得到答案了。

具体代码如下(方法二):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=150005;
int t,T,n,m;
int a[N],to[N][25];
int sa[N],rk[N],tp[N],tax[N];
void qsort()
{
	memset(tax,0,sizeof(tax));
	for(int i=1;i<=n;++i)
	tax[rk[i]]++;
	for(int i=1;i<=m;++i)
	tax[i]+=tax[i-1];
	for(int i=n;i>=1;--i)
	sa[tax[rk[tp[i]]]--]=tp[i];
}
void SA()
{
	for(int i=1;i<=n;++i)
	{
		rk[i]=a[i];
		tp[i]=i;
	}
	m=10;
	qsort();
	for(int k=0;(1<<k)<=n;++k)
	{
		memset(tax,0,sizeof(tax));
		for(int i=1;i<=n;++i)
		tax[rk[to[i][k]]]++;
		for(int i=1;i<=m;++i)
		tax[i]+=tax[i-1];
		for(int i=1;i<=n;++i)
		tp[tax[rk[to[i][k]]]--]=i;
		qsort();
		swap(tp,rk);
		int tmp=rk[sa[1]]=1;
		for(int i=2;i<=n;++i)
		{
			if(tp[sa[i-1]]==tp[sa[i]]&&tp[to[sa[i-1]][k]]==tp[to[sa[i]][k]])
			rk[sa[i]]=tmp;
			else
			rk[sa[i]]=++tmp;
		}
		m=tmp;
		if(m>=n)
		break;
	}
}
int main()
{
	cin>>T;
	while(++t<=T)
	{
		cin>>n;
		for(int i=1;i<=n;++i)
		{
			scanf("%1d",&a[i]);
			++a[i];
			to[i][0]=((ll)(i-1)*(i-1)+1)%n+1;
		}
		for(int i=1;i<=20;++i)
		{
			for(int j=1;j<=n;++j)
			{
				to[j][i]=to[to[j][i-1]][i-1];
			}
		}
		SA();
		printf("Case #%d: ",t);
		int tmp=sa[n];
		for(int i=1;i<=n;++i)
		{
			printf("%d",a[tmp]-1);
			tmp=to[tmp][0];
		}
		printf("
");
	}
}

以上是关于题解 HDU6223 Infinite Fraction Path的主要内容,如果未能解决你的问题,请参考以下文章

HDU 6223 Infinite Fraction Path(BFS+剪枝)

hdu 6223 Infinite Fraction Path

Infinite Fraction Path(HDU6223 + bfs + 剪枝)

HDU6223——2017ICPC徐州G Infinite Fraction Path

hdu6223 Infinite Fraction Path 2017沈阳区域赛G题 bfs加剪枝(好题)

HDOJ6223Infinite Fraction Path(后缀数组)