這是我給我們隊本場比賽的唯一貢獻,而且還帶着 3 發罰時 /kk。
考慮一個字符串的最長后綴怎么求,顯然一個后綴排序就完事了,可是這並不能拓展到一個字符串的所有前綴,所以他 GG 了。
所以考慮 lyndon 分解,那么最小后綴就是分解得到的最后一個 lyndon word 。證明略
所以要計算出字符串的所有前綴的 lyndon 分解,考慮 Duval 算法的過程,設當前前綴的末尾是 \(j\)
- 如果 \(j\) 是原串 lyndon 分解的一段終點,那么他的答案就是這段的起點
- 否則,設當前維護的近似 lyndon 串的長度為 \(len\) ,最小周期為 \(d\)
-
- 若 \(len=d\) ,則 \(ans_j=j-len\) ,即當前的近似 lyndon 串是前綴 \([1...j]\) 分解得到的最后一個 lyndon word
- 否則 \(ans_j=ans_{j-d}+d\) ,即與上一個周期答案相對位置相同,證明:顯然不會有這個周期之前的答案優於他,否則他就應該被前一個 lyndon word 包含;所以只與最后一個周期有關,必然是這樣。
題外話:lyndon 分解那套理論真的是 easy 嗎
Code:
#define N 1000005
char s[N];
int ans[N],tans[N];
void work()
{
scanf("%s",s+1);
int n=strlen(s+1);
int cnt=0;
for(int i=1;i<=n;)
{
int j=i+1,k=i;
ans[i]=i;
while(j<=n&&s[k]<=s[j])
{
if(k==i) ans[j-1]=i;
else ans[j-1]=ans[k-1]+(j-k);
if(s[k]==s[j]) k++;
else k=i;
j++;
}
while(i<=k)
{
i+=j-k;
tans[++cnt]=i-1;
}
}
for(int i=1;i<=cnt;i++) ans[tans[i]]=tans[i-1]+1;
int Ans=0,tmp=1;
for(int i=1;i<=n;i++)
{
Ans=add(Ans,mul(tmp,ans[i]));
tmp=mul(tmp,1112);
}
printf("%d\n",Ans);
}
signed main()
{
int T=read(); while(T--) work();
return 0;
}