题目描述
给你一个长度为N的串,求每个前缀的字典序最小的后缀的位置。
分析
这个题可以用SAM做,但是会被卡,所以考虑用Lyndon分解来做
不懂Lyndon分解的可以看看我巨佬学弟博客的学习笔记
发现对于Lyndon分解完后的一个Lyndon串,整个Lyndon串的最小前缀肯定就是这个Lyndon串的开头。
我们可以对每一个Lyndon串做一次exkmp,这样假设从后往前扫,假设一个位置是
,然后和这个串前缀相同的长度是
,那么他就可以对所有
中没有记录过答案的串记录答案。
其实也可以在做Lyndon分解中直接处理,我们假设
是要拓展的字符,
是
的在Lyndon串中相对应的位置,统计答案的时候,假设:
那么肯定
这个位置可以和前面的Lyndon串合并,形成新的Lyndon串,所以记录答案
由于
和
是一样的,那么
这个位置的答案就相应继承
的答案就好了,
最后如果在
移动后,新建的一个Lyndon串中,
就好了
代码
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define fi first
#define se second
#define CL clear
#define MP make_pair
#define PB push_back
#define int long long
const int N = (int) 1e6 + 10;
const int mod = (int) 1e9+7;
using namespace std;
typedef long long ll;
inline int rd() {
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f*=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
char s[N]; int p[N];
signed main() {
int t = rd();
while(t--) {
scanf("%s",s+1); int n = strlen(s+1);
for(int i=1;i<=n;) {
int j = i; int k = i+1; p[i] = i;
while(j<=n && s[j] <= s[k]) {
if(s[j] < s[k]) j = i,p[k] = i;
else p[k] = p[j] + k - j , j++;
k++;
}while(i<=j) i+=k-j;
}
int bas = 1; int ans = 0;
for(int i=1;i<=n;++i) ans = (ans + bas * p[i] % mod) % mod , bas = bas * 1112 % mod;
printf("%lld\n",ans);
for(int i=1;i<=n;++i) p[i] = 0;
}
return 0;
}