洛谷4051 JSOI2007 字符加密(SA)

真是一道良好的SA模板题

首先,由于涉及到从左边移动到右边这个过程,我们不妨直接把字符串复制一遍,接在后面。
然后直接构造后缀数组,按排名从小到大,枚举所有的位置,如果这个后缀的起始点是在原串中的,那么就输出当前后缀的起始点往后第n个字符,就能直接解决了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 2e6+1e2;
int rk[maxn],wb[maxn],sa[maxn];
int tmp[maxn],h[maxn],height[maxn];
int n;
char a[maxn];
void getsa()
{
 int *x =rk,*y=tmp;
 int s = 128;
 for (int i=1;i<=n;i++) x[i]=a[i],y[i]=i;
 for (int i=1;i<=s;i++) wb[i]=0;
 for (int i=1;i<=n;i++) wb[x[y[i]]]++;
 for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
 for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i];
 int p =0;
 for (int j=1;p<n;j<<=1)
 {
  p=0;
  for (int i=n-j+1;i<=n;i++) y[++p]=i;
  for (int i=1;i<=n;i++) if (sa[i]>j) y[++p] = sa[i]-j;
  for (int i=1;i<=s;i++) wb[i]=0;
  for (int i=1;i<=n;i++) wb[x[y[i]]]++;
  for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
     for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i];
  swap(x,y);
  p=1;
  x[sa[1]]=1;
  for (int i=2;i<=n;i++)
  {
   x[sa[i]] = (y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j]) ? p : ++p; 
   } 
   s=p;
 }
 for (int i=1;i<=n;i++) rk[sa[i]]=i;
 h[0]=0;
    for (int i=1;i<=n;i++)
 {
  h[i]=max(h[i-1]-1,0);
  while(i+h[i]<=n && sa[rk[i]-1]+h[i]<=n && a[i+h[i]]==a[sa[rk[i]-1]+h[i]]) h[i]++;
 } 
 for (int i=1;i<=n;i++) height[i]=h[sa[i]];
}
int main()
{
  scanf("%s",a+1);
  n=strlen(a+1);
  for (int i=1;i<=n;i++) a[i+n]=a[i];
  n*=2;
  getsa();
  for (int i=1;i<=n;i++)
  {
   if (sa[i]<=n/2) cout<<a[sa[i]+n/2-1];
  }
  return 0;
}

posted @ 2018-12-22 18:21  y_immortal  阅读(109)  评论(0编辑  收藏  举报