B-Suffix Array
题意:
一个字符串只含有a和b,先给出b数组的构造方式:
对于每个位置i来说:
- 如果存在一个位置j,使得j<i,且s[j] == s[i],则b[i]=i-j
- 否则b[i]=0
现在对字符串每个后缀都构造B数组,并按照字典序排序
题解:
参考博客
题目标题就已经透露了一切,Suffix Array,说明这个题要用后缀数组来做,但是具体怎么做呢?
我们会发现,化为B数组的值后,第一个a的值肯定为0,第一个b的值肯定也为0,那么从第一个a到第一个b之间肯定都是1
比如“aaaabaaab”,则B(t) = 011102114,前半段为01110(也就是第一个a到第一个b),我们可以将B数组分为两部分,前半部为01110,后半部分为2114,这样字典序排序时,我们先对前半部分排序即可,如果前半部分一样再对后半部分排序
对于前半部分排序,直接比较长度即可,因为都是0开头,0结尾,中间是1,越长说明中间的1越多,且前半部分极好确定,直接找第一个a与b就行
那现在后半部分怎么确定呢?
Ai表示前半部分,DI表示后半部分
后半部分是数组B的后缀,我们直接后缀数组,得到rank值,rank[i+|Ai|]就是Di的排名
Rank[i]=后缀suf(i)的排名,也就是从第i位到最后一位构成的子串排名
这个题,秒极了
代码:
#include <bits/stdc++.h>
using namespace std;
struct _IO{_IO(){ios::sync_with_stdio(0);cin.tie(0);}}_io;
typedef long long ll; typedef long double db;
const int N = 2e6 + 5, M = 1e9 + 7;int sa[N], rk[N], oldrk[N << 1], id[N], px[N], cnt[N];
// px[i] = rk[id[i]](用于排序的数组所以叫 px)bool cmp(int x, int y, int w) {return oldrk[x] == oldrk[y] && oldrk[x + w] == oldrk[y + w];
}void da(int *s, int n, int m) {int i,p,w;for(int i=0;i<=n;i++)cnt[i]=0;for (i = 1; i <= n; ++i) ++cnt[rk[i] = s[i]];for (i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];for (i = n; i >= 1; --i) sa[cnt[rk[i]]--] = i;for (w = 1; w < n; w <<= 1, m = p) { // m=p 就是优化计数排序值域for (p = 0, i = n; i > n - w; --i) id[++p] = i;for (i = 1; i <= n; ++i)if (sa[i] > w) id[++p] = sa[i] - w;//memset(cnt, 0, sizeof(cnt));for(int i=0;i<=n;i++)cnt[i]=0;for (i = 1; i <= n; ++i) ++cnt[px[i] = rk[id[i]]];for (i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];for (i = n; i >= 1; --i) sa[cnt[px[i]]--] = id[i];for(int i=0;i<=n;i++)oldrk[i]=rk[i];//memcpy(oldrk, rk, sizeof(rk));for (p = 0, i = 1; i <= n; ++i)rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p;}}int n;
struct node {int x, y;bool operator < (const node &b) const {if (y - x == b.y - b.x) {return rk[y+1] < rk[b.y+1];}return y - x < b.y - b.x;}
} a[N];
int b[N];
char s[N];
int main() {while (cin >> n) {cin >> s+1;int x = -1, y = -1;for (int i = 1; i <= n; i++) {b[i] = 0;if (s[i] == 'a') {if (x != -1) b[i] = i - x;x = i;} else {if (y != -1) b[i] = i - y;y = i;}}for(int i=1;i<=n;i++){b[i]++;}da(b, n, n);//后缀数组求出rank数组for(int i=1;i<=n;i++)x = y = n+1;for (int i = n ; i >= 1; i--) {if (s[i] == 'a') {//从第i位开始的后缀,分为前部分Ai,范围[x,y]和后部分Bi a[i] = {i, y};x = i;} else {a[i] = {i, x};y = i;}}rk[n+1] = -1;rk[n+2] = -2;sort(a+1, a + n+1);for (int i = 1; i <=n; i++) {cout << a[i].x << ' ';}cout << '\n';}
}