题目描述:给定一个长为 (n) 的字符串 (S),求它所有前缀的循环移位最小表示法的开头位置,相同的输出靠前的一个。
数据范围:(nle 3 imes 10^6)
好像无论怎么想都跟朴素暴力一样是 (O(n^2)) 的...于是官方题解就开始分析性质...
我们考虑 (k=1 ightarrow n) 计算答案,并且只保留一些在将来有可能成为答案的点,其他的直接扔掉。我们称这些留下的点为候选点。
性质1:对于 (i<j),若 ( ext{lcp}(S[i:k],S[j:k])le k-j),则 (i,j) 中至少有一个不为候选点。
这个很显然,就是 (i,j) 在 (k) 之前就已经比较出了大小,那么一定会淘汰一个。
性质2:对于 (i<j),若 ( ext{lcp}(S[i:k],S[j:k])>k-j),且 (k-jge j-i),则 (j) 不为候选点。
这个就需要考虑一下了,首先我们设 (S=S_1S_1S_2),其中 (S_1,S_2) 为 (S) 的两个子串。
则对于 (S) 的三个循环移位:(S_1S_1S_2,S_1S_2S_1,S_2S_1S_1),那么 (S_1S_2S_1) 一定比其他两个之一小,也就是说不可能为候选点。
那么如果有 (k-jge j-i),所以 (S[1:k]=ABBC),则设 (S_1=B,S_2=CA),也就是 (BCAB) 不可能成为答案,所以 (j) 不为候选点。
这样就可以得出任意两个候选点 (i,j),(2(k-j)<k-i),所以用这两个性质计算出的候选点只有 (O(log n)) 个。
然而为了方便计算还需要知道一点。
性质3:对于两个候选点 (i<j),(S[j:k]) 为 (S[i:k]) 的前缀。
这个可以由性质1直接得出。
所以在比较候选点之间的大小的时候,不需要比较开始的一部分,剩下的可以转化为求两段后缀与整串的 lcp,这个就是 ExKMP 的 nxt 数组。
时间复杂度 (O(nlog n)),空间复杂度 (O(n))。
#include<bits/stdc++.h>
#define Rint register int
using namespace std;
const int N = 3000003;
int n, now, p0, nxt[N], ans;
char str[N];
vector<int> cur, tmp;
inline int cmp(int p, int len){
return nxt[p] >= len ? 0 : (str[nxt[p]] < str[p + nxt[p]] ? 1 : -1);
}
inline int cmp(int x, int y, int len){
int res;
if(res = cmp(len - x + y, x - y)) return res > 0 ? x : y;
if(res = cmp(x - y, y)) return res > 0 ? y : x;
return y;
}
int main(){
scanf("%s", str); n = strlen(str);
nxt[0] = n; now = 0;
while(str[now] == str[now + 1] && now + 1 < n) ++ now;
nxt[1] = now; p0 = 1;
for(Rint i = 2;i < n;++ i){
if(i + nxt[i - p0] < p0 + nxt[p0]) nxt[i] = nxt[i - p0];
else {
now = max(0, p0 + nxt[p0] - i);
while(str[now] == str[now + i] && now + i < n) ++ now;
nxt[i] = now; p0 = i;
}
}
for(Rint k = 0;k < n;++ k){
tmp.resize(1); tmp[0] = k;
for(Rint i : cur){
while(!tmp.empty() && str[i + k - tmp.back()] < str[k]) tmp.pop_back();
if(tmp.empty() || str[i + k - tmp.back()] == str[k]){
while(!tmp.empty() && k - tmp.back() >= tmp.back() - i) tmp.pop_back();
tmp.push_back(i);
}
}
cur = tmp; ans = cur[0];
for(Rint i = 1;i < cur.size();++ i) ans = cmp(ans, cur[i], k + 1);
printf("%d%c", ans + 1, "
"[k == n - 1]);
}
}