题目:传送门
题意
给你一个字符串,求每个前缀的最小后缀对应的起始位置。
总的字符串长度 <= 2e7
思路
Lydon 分解: 参考博客
Lyndon 串:对于字符串x,如果x的字典序严格小于x的所有后缀的字典序,我们称x是简单串,或者Lyndon串。
近似Lyndon串: 若x为Lyndon串,则xxxx′为近似Lyndon串,x′为x的前缀。
Lyndon分解: 将一个串x分作x1x2..xk,每一个部分都是Lyndon串,且 xi >= xi+1
定理:
Lyndon串是循环同构中字典序最小的一个。
Lyndon分解唯一。
两个Lyndon串a,b若a<b,有ab为Lyndon串。
通过 Lyndon 分解 解此题: 参考博客
#include <bits/stdc++.h> #define LL long long #define ULL unsigned long long #define UI unsigned int #define mem(i, j) memset(i, j, sizeof(i)) #define rep(i, j, k) for(int i = j; i <= k; i++) #define dep(i, j, k) for(int i = k; i >= j; i--) #define pb push_back #define make make_pair #define INF 0x3f3f3f3f #define inf LLONG_MAX #define PI acos(-1) #define fir first #define sec second #define lb(x) ((x) & (-(x))) #define dbg(x) cout<<#x<<" = "<<x<<endl; using namespace std; const int N = 1e6 + 5; const LL mod = 1e9 + 7; int n; char a[N]; LL ans[N]; void solve() { scanf("%s", a); n = strlen(a); int i = 0, j, k; ans[0] = 1LL; while(i < n) { j = i + 1, k = i; while(j < n && a[k] <= a[j]) { if(a[k] == a[j]) { ans[j] = j + ans[k] - k; k++; } else { k = i; ans[j] = i + 1; } j++; } while(i <= k) { i += j - k; } if(i == j && i < n) ans[j] = i + 1; } LL res = 0LL; dep(i, 0, n - 1) { res = (res * 1112LL % mod + ans[i]) % mod; } printf("%lld ", res); } int main() { int _; scanf("%d", &_); while(_--) solve(); // solve(); return 0; }