传送门
设做右端点现在分别是 l , r l,r l,r
假如两头不相等,输出小的那个
否则,我们找到一个最小的 l e n len len满足 a [ l + l e n − 1 ] ! = a [ r − l e n + 1 ] a[l+len-1]!=a[r-len+1] a[l+len−1]!=a[r−len+1]
那么比较谁比较小就优先拿那一边的。
不过是直接拿 l e n len len个字符吗??
当然不是,可能左右循环拿更加划算,所以每一次都需要二分判断
#include <bits/stdc++.h>
using namespace std;
#define ULL unsigned long long
const ULL base = 131;
const int maxn = 1e6+10;
ULL p[maxn],h1[maxn],h2[maxn];
int n,top;
char a[maxn],b[maxn];
ULL get1(int l,int r )
{
return h1[r]-h1[l-1]*p[r-l+1];
}
ULL get2(int l,int r )
{
return h2[l]-h2[r+1]*p[r-l+1];
}
int main()
{
cin >> n; getchar();
p[0] = 1;
for(int i=1;i<=n;i++)
{
scanf("%c",&a[i] ); getchar();
p[i] = p[i-1]*base;
h1[i] = h1[i-1]*base+a[i];
}
for(int i=n;i>=1;i--) h2[i] = h2[i+1]*base+a[i];
int l = 1, r = n;//两边都没有拿走
while( r>=l )
{
if( a[l]<a[r] ) b[++top] = a[l++];
else if( a[r]<a[l] ) b[++top] = a[r--];
else//两边相等
{
int L = 1, R = r-l+1, len = r-l+1;
while( R>=L )
{
int mid = L+R>>1;
if( get1(l,l+mid-1)==get2(r-mid+1,r) ) L = mid+1;
else R=mid-1,len=mid;
}
if( a[l+len-1]<a[r-len+1] ) b[++top] = a[l++];
else b[++top] = a[r--];
}
}
for(int i=1;i<=top;i++)
{
cout << b[i];
if( i%80==0 ) cout << endl;
}
}
但是其实也可以后缀数组做
考虑当 a [ l ] = = a [ r ] a[l]==a[r] a[l]==a[r]时,正着取好还是反着取好???
其实本质是比较这个串和它的反串哪个字典序比较大
这不就是后缀数组吗!!!
设一开始的字符串是 s s s, s s s的反串为 t t t
那么构造新字符串 s + t s+t s+t
那么 r k [ i ] rk[i] rk[i]就是 [ i , n ] [i,n] [i,n]的排名
那么 r k [ i + n ] rk[i+n] rk[i+n]就是 [ i , n ] [i,n] [i,n]的反串字典序排名
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e6+10;
char a[maxn];
int x[maxn],y[maxn],sa[maxn],c[maxn],n,m,rk[maxn];
void get_sa()
{
for(int i=1;i<=n;i++) c[x[i]=a[i]]++;
for(int i=2;i<=m;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 num = 0;
for(int i=n-k+1;i<=n;i++) y[++num] = i;
for(int i=1;i<=n;i++) if( sa[i]>k ) y[++num] = sa[i]-k;
for(int i=1;i<=m;i++) c[i] = 0;
for(int i=1;i<=n;i++) c[x[i]]++;
for(int i=2;i<=m;i++) c[i] += c[i-1];
for(int i=n;i>=1;i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
swap(x,y);
x[sa[1]] = 1; num = 1;
for(int i=2;i<=n;i++)
x[sa[i]] = ( y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k] )?num:++num;
if( num==n ) break;
m = num;
}
for(int i=1;i<=n;i++) rk[sa[i]] = i;
}
int main()
{
cin >> n; m = 127; getchar();
for(int i=1;i<=n;i++)
scanf("%c",&a[i] ),getchar();
for(int i=1;i<=n;i++) a[2*n-i+1] = a[i];
n+=n; get_sa();
for(int i=1,l=1,r=n/2;i<=n/2;i++)
{
if( rk[l]<rk[n-r+1] ) cout << a[l++];
else cout << a[r--];
if( i%80==0 ) cout << endl;
}
}