1. 程式人生 > >POJ-3693--字尾陣列求字典序最小重複次數最多子串

POJ-3693--字尾陣列求字典序最小重複次數最多子串

摘自2009年國家集訓隊羅穗騫論文《字尾陣列-處理字串的有力工具


因此我們只需處理出原串正反兩個版本的字尾陣列,然後用rmq問題的處理方法求LCP,我的處理方法是讓關鍵點包含在r中,如果想包含在l中,只需把向前向後擴充套件的位置稍作處理&查詢rank最小值的區間端點分別+1即可。

字典序最小的處理方法摘自hzwer部落格orz

因為每次我們找到了合法答案,子串起點∈[i-l,i-l+(l+r)%L],那麼這個區間中rank的最小值就是字典序最小起點,sa[min{rank}]即為答案起點

美到令人窒息的程式碼

#include<bits/stdc++.h>
#define cls(x) memset(x,0,sizeof x)
using namespace std;
#define debug(x) cout<<#x<<"="<<x<<endl
const int inf = 0x3f3f3f3f;
const int maxn = 100009;
const int Log = 17;
int ft[maxn],n,ans,ord,ansl,ansr;
char s[maxn];
struct ST
{
	int mn[maxn][Log+1];
	void clear(){memset(mn,0,sizeof mn);}
	void calc(int *a)
	{
		for(int i=1;i<=n;i++) mn[i][0]=a[i];
		for(int i=1;i<=Log;i++)
		   for(int j=1;j<=n-(1<<i>>1);j++)
		      mn[j][i]=min(mn[j][i-1],mn[j+(1<<i>>1)][i-1]);
	}
	int query(int l,int r)
	{
		int ll=(r-l+1);
		ll=ft[ll];
		return min(mn[l][ll],mn[r-(1<<ll)+1][ll]);
	}
}rk;
struct Suffix_Array
{
	int sa[maxn],rank[maxn],t[maxn],t2[maxn],c[maxn],height[maxn];
	ST mn;
	void init()
	{
		cls(sa);cls(rank);cls(height);cls(c);
		cls(t);cls(t2);mn.clear();
	}
	void build()
	{
		int mr=30,*x=t,*y=t2;
		for(int i=1;i<=mr;i++) c[i]=0;
		for(int i=1;i<=n;i++) c[x[i]=s[i]-'a'+1]++;
		for(int i=1;i<=mr;i++) c[i]+=c[i-1];
		for(int i=n;i>=1;i--) sa[c[x[i]]--]=i;
		for(int k=1;k<=n;k<<=1)
		{
			int p=0;
			for(int i=n-k+1;i<=n;i++) y[++p]=i;
			for(int i=1;i<=n;i++) if(sa[i]>k) y[++p]=sa[i]-k;
			for(int i=1;i<=mr;i++) c[i]=0;
			for(int i=1;i<=n;i++) c[x[y[i]]]++;
			for(int i=1;i<=mr;i++) c[i]+=c[i-1];
			for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i];
			swap(x,y);
			p=1;x[sa[1]]=1;
			for(int i=2;i<=n;i++)
			   if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]) x[sa[i]]=x[sa[i-1]];
			   else x[sa[i]]=++p;
			if(p>=n) break;
			mr=p;
		}
		for(int i=1;i<=n;i++) rank[sa[i]]=i;
		int h=0;
		for(int i=1;i<=n;i++)
		{
			if(rank[i]==1) h=0;
			else
			{
				int k=sa[rank[i]-1];
				h--;if(h<0) h=0;
				while(s[i+h]==s[k+h]) h++;
			}
			height[rank[i]]=h;
		}
		mn.calc(height);
	}
	int lcp(int x,int y)
	{
		x=rank[x];y=rank[y];
		if(x>y) swap(x,y);x++;
		return mn.query(x,y);
	}
}a,b;
void ClearLove(){ans=1;ord=inf;a.init();b.init();rk.clear();}
void solve(int L)
{
	for(int i=1;i+L<=n;i+=L)
		if(s[i]==s[i+L])
		{
			int l=b.lcp((n-i+1)+1,(n-i-L+1)+1),r=a.lcp(i,i+L);
			int k=(l+r)/L+1;
			if(k>ans) ans=k,ord=inf;
			if(k==ans)
			{
				int t=rk.query(i-l,i-l+(l+r)%L);
				if(t<ord)
				{
					ord=t;
					ansl=a.sa[t];ansr=ansl+ans*L-1;
				}
			}
		}
}
int main()
{
	for(int i=0;i<=Log;i++) ft[1<<i]=i;
	for(int i=1;i<=maxn-9;i++) if(!ft[i]) ft[i]=ft[i-1];
	int cas=0;
	while(scanf("%s",s+1))
	{
		if(s[1]=='#') break;
		ClearLove();n=strlen(s+1);
		cas++;printf("Case %d: ",cas);
		a.build();reverse(s+1,s+1+n);b.build();
		rk.calc(a.rank);reverse(s+1,s+1+n);
		for(int i=1;i<=n;i++)
		   if(a.rank[i]<ord) ord=a.rank[i],ansl=ansr=i;
		for(int i=1;i<=n;i++) solve(i);
		for(int i=ansl;i<=ansr;i++) putchar(s[i]);
		puts("");
	}
	return 0;
}
//Hash_table 2017/1/22
//srO lct1999 Orz
//srO hzwer Orz