题目传送门
题意:
给你一个字符串,输出加密后的字符串。
例如‘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") ;
}
来源:CSDN
作者:敲代码的欧文
链接:https://blog.csdn.net/Irving0323/article/details/104442243