题意
传送门 POJ 1509
题解
求长度为 n n n 的字符串构成的环任一位置拆分成的 n n n 条链中,字典序最小的,若有多个答案,则返回拆开位置索引最小的。将原串复制一份连接到自身后面,求后缀数组,实现上使用基于倍增的方法。答案为满足长度大于等于 n n n 的最小后缀的长度为 n n n 的前缀。
为了满足拆开位置索引最小的要求,有两种思路:求解后缀数组时,将超出后缀长度的部分赋最大的权值,此时保证按字典序排序时,较短串为较长串前缀时,较短串字典序比较长串更大;或者直接在复制拼接的字符串后面添加一个字典序最大的字符(如 ′ z ′ 'z' ′z′),使除了最后添加的字符以外,较短串为较长串前缀时,较短串字典序比较长串更大。
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
#define maxn 10005
string S;
int n, k, rnk[maxn << 1], tmp[maxn << 1], sa[maxn << 1];
bool cmp(int i, int j)
{
if (rnk[i] != rnk[j])
return rnk[i] < rnk[j];
int ri = i + k <= n ? rnk[i + k] : maxn;
int rj = j + k <= n ? rnk[j + k] : maxn;
return ri < rj;
}
void construct_sa(string s, int *sa)
{
n = s.length();
for (int i = 0; i <= n; ++i)
{
sa[i] = i;
rnk[i] = i < n ? s[i] : maxn;
}
for (k = 1; k < n; k <<= 1)
{
sort(sa, sa + n + 1, cmp);
tmp[sa[0]] = 0;
for (int i = 1; i <= n; ++i)
tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]) ? 1 : 0);
memcpy(rnk, tmp, sizeof(int) * (n + 1));
}
}
void solve()
{
int len = S.length();
S += S;
construct_sa(S, sa);
for (int i = 0; i <= n; ++i)
{
if (sa[i] < len)
{
cout << sa[i] + 1 << endl;
return;
}
}
}
int main()
{
int t;
cin >> t;
while (t--)
{
cin >> S;
solve();
}
return 0;
}