【JSCPC2021】Reverse the String(Lyndon 理論)
阿新 • • 發佈:2022-01-16
題意
給定字串 \(s\),問任意翻轉一個區間 \([l,r]\) 後得到的最小字串。
\(\sum |s| \le 1.5\times 10^6\)。
分析
依次考慮答案第 \(i\) 位能否比 \(s_i\) 小可得:\(l\) 一定是右邊有字元比它小的最左位置。因此只需考慮翻一個字首的情況,可以用二分+雜湊比較出最小答案,但是有線性做法。
記 \(t=s^R\),我們要把 \(t\) 劃分成 \(t_1t_2\),使得 \(t_2t_1^R\) 最小。
直接在 \(t\) 上跑 Duval 演算法。記當前位為 \(i\),和它比較的位為 \(j\)。當出現 \(t_i<t_j\)
最終 \(t\) 的剩餘部分能劃分為 \(w^pw'\) 的形式,其中 \(w\) 為 lyndon 串且 \(w'\) 為 \(w\) 的可空字首,而 \(t_2\) 的開頭一定是某個 \(w\) 或 \(w'\) 的開頭。
考慮三個串 \(x,y,z\) 且 \(|x|=|y|\)。那麼 \(xzy\) 不可能比 \(xxz,zyy\) 都小,否則 \(xz>zy\) 和 \(xz<zy\) 同時成立匯出矛盾。代入 \(x=w^R,y=w,z=w'\)
實現
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=(a),_=(b);i<=_;++i) #define per(i,a,b) for(int i=(a),_=(b);i>=_;--i) using namespace std; typedef long long ll; typedef unsigned long long ul; typedef reverse_iterator<int*>RI; #define pb push_back #define IL inline const int mod=998244353; IL int inc(int x,int y){return x+=y-mod,x+=x>>31&mod;} IL int dec(int x,int y){return x-=y,x+=x>>31&mod;} IL int mul(int x,int y){return ul(x)*y%mod;} IL int idk(int x,int y,int p){return p&1?dec(x,y):inc(x,y);} IL int ksm(int x,int y,int p=1){ for(;y;y>>=1,x=mul(x,x))if(y&1)p=mul(x,p); return p; } //head const int N=1e5+5; int T,n,m,mn[N],l; char s[N],t[N],res[N],tmp[N]; void upd(int r){ rep(i,1,n+1)tmp[i]=s[i]; reverse(tmp+l,tmp+r+1); if(strcmp(tmp+1,res+1)<0)rep(i,1,n)res[i]=tmp[i]; } int main(){ scanf("%d",&T); while(T--){ scanf("%s",s+1),n=strlen(s+1); mn[n]=s[n]-'a'; per(i,n-1,1)mn[i]=min(mn[i+1],s[i]-'a'); l=m=0; rep(i,1,n)if(mn[i]<s[i]-'a'){l=i;break;} if(!l){puts(s+1);continue;} per(i,n,l)t[++m]=s[i]; rep(i,1,n+1)res[i]=s[i]; for(int i=2,j=1,k=1;i<=m+1;){ if(i==m+1){ upd(n+1-k); while(k<j)k+=i-j; upd(n+1-k); puts(res+1); break; } if(t[i]==t[j]){ ++i,++j; }else if(t[i]>t[j]){ ++i,j=k; }else{ while(k<=j)k+=i-j; i=j=k,++i; } } } exit(0); }