P4051 后缀数组

家住魔仙堡 提交于 2020-02-22 16:07:21

题目传送门

题意:

给你一个字符串,输出加密后的字符串。

例如‘JSOI07’,可以读作: JSOI07 SOI07J OI07JS I07JSO 07JSOI 7JSOI0 把它们按照字符串的大小排序: 07JSOI 7JSOI0 I07JSO JSOI07 OI07JS SOI07J 读出最后一列字符:I0O7SJ,就是加密后的字符串。

数据范围:字符串的长度不超过 

 。

题解:

给一个字符串 

,我们扩大二倍,变成 

 。

然后考虑后缀排序,输出想要的东西。

设 

 。

那么当后缀长度大于 

 时,后面多出的部分不会影响排序。

简单分析一下:

设 

 ,则 

 。

现在有一个后缀是 

 。

我们想要的其实是 

,分析最后的三个字符 

 的影响。

观察发现 

 是 

 的前缀,那也就是说后面不想要的部分是想要的部分的前缀,那就由自身决定了,那就没影响了。

然后掏出后缀数组板子,求出sa就行了。

感受:

反复看了几遍都是5个0,结果1e5是RE,开成1e6才过去。

代码:

#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 1e6 + 5 ;
int rk[maxn << 1] , sa[maxn << 1] , height[maxn << 1] ;
int tmp[maxn << 1] , cnt[maxn] ;
char s[maxn] ;
void suffixarray(int n , int m)
{
   n ++ ;
   for(int i = 0 ; i < n * 2 + 5 ; i ++)
     rk[i] = sa[i] = height[i] = tmp[i] = 0 ;//开2 倍空间
   for(int i = 0 ; i < m ; i ++)  cnt[i] = 0 ;
   for(int i = 0 ; i < n ; i ++)  cnt[rk[i] = s[i]] ++ ;
   for(int i = 1 ; i < m ; i ++)  cnt[i] += cnt[i - 1] ;
   for(int i = 0 ; i < n ; i ++)  sa[-- cnt[rk[i]]] = i ;
   for(int k = 1 ; k <= n ; k <<= 1)
   {
     int j = 0 ;
     for(int i = 0 ; i < n ; i ++)
     {
       j = sa[i] - k ;
       if(j < 0)  j += n ;
       tmp[cnt[rk[j]] ++] = j ;
     }
     sa[tmp[cnt[0] = 0]] = j = 0 ;
     for(int i = 1 ; i < n ; i ++)
     {
       if(rk[tmp[i]] != rk[tmp[i - 1]]
       || rk[tmp[i] + k] != rk[tmp[i - 1] + k])
         cnt[++ j] = i ;
       sa[tmp[i]] = j ;
     }
     memcpy(rk , sa , n * sizeof(int)) ;
     memcpy(sa , tmp , n * sizeof(int)) ;
     if(j >= n - 1)  break ;
   }
   height[0] = 0 ;
   for(int i = 0 , k = 0 , j = rk[0] ; i < n - 1 ; i ++ , k ++)
     while(~k && s[i] != s[sa[j - 1] + k])
       height[j] = k -- , j = rk[sa[j] + 1] ;
}
int main()
{
   scanf("%s" , s) ;
   int len = strlen(s) ;
   for(int i = 0 ; i < len ; i ++)
     s[i + len] = s[i] ;
   suffixarray(2 * len , 200) ;
   for(int i = 1 ; i <= 2 * len ; i ++)
     if(sa[i] >= 1 && sa[i] <= len)
       printf("%c" , s[sa[i] + len - 1]) ;
   printf("\n") ;
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!