题目大意:
有一个长度为
的字符串
,和
中第
个字符,定义子串
为一个关于
的识别子串,当且仅当
在
中只出现一次。
计算对于一个字符串S,关于S的每一位的最短识别子串分别有多长。
分析:
后缀数组可以求出以第
位开头的最短的在原串中只出现过一次的子串
子串的长度是
所以我们枚举每个位置
,找到这个串,然后考虑它的贡献:
对于这个串之内的位置,答案可以用这个串的长度更新;
对于这个串右边的位置,串可以向右“延伸”直到包含该位置(延伸后的串显然也只出现过一次),所以答案可以用
来更新
然后用线段树维护即可
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define fo(i, j, k) for (int i = j; i <= k; i++)
#define fd(i, j, k) for (int i = j; i >= k; i--)
#define inf 0x3f3f3f3f
#define N 500005
using namespace std;
struct Node{int rp, cp;}tree[N*4];
int height[N], rank[N*2], sa[N], s[N], b[N], c[N], d[N], cmax, cmin, n, m;
char str[N];
bool bz;
void Get_SA() {
int i, x = 0;
n = strlen(str + 1), m = 30;
fo(i, 1, n) b[s[i] = str[i]-'a'+1]++;
fo(i, 1, m) b[i] += b[i-1];
fd(i, n, 1) c[b[s[i]]--] = i;
fo(i, 1, n) {
if (s[c[i]] != s[c[i-1]]) x++;
rank[c[i]] = x;
}
for (int j = 1; j <= n; j <<= 1) {
fo(i, 1, n) b[i] = 0;
fo(i, 1, n) b[rank[i+j]]++;
fo(i, 1, n) b[i] += b[i-1];
fd(i, n, 1) c[b[rank[i+j]]--] = i;
fo(i, 1, n) b[i] = 0;
fo(i, 1, n) b[rank[i]]++;
fo(i, 1, n) b[i] += b[i-1];
fd(i, n, 1) d[b[rank[c[i]]]--] = c[i];
x = 0;
fo(i, 1, n) {
if (rank[d[i]] != rank[d[i-1]] || rank[d[i]] == rank[d[i-1]] && rank[d[i]+j] != rank[d[i-1]+j]) x++;
c[d[i]] = x;
}
fo(i, 1, n) rank[i] = c[i];
if (x == n) break;
}
}
void Get_height() {
int i, x = 0;
fo(i, 1, n) sa[rank[i]] = i;
fo(i, 1, n) {
if (x) x--;
int j = sa[rank[i]-1];
while (i+x <= n && j+x <= n && s[i+x] == s[j+x]) x++;
height[rank[i]] = x;
}
height[1] = 0;
}
void build(int x, int l, int r) {
tree[x].cp = -inf; tree[x].rp = inf;
if (l == r) return;
int mid = (l+r)>>1;
build(x*2, l, mid), build(x*2+1, mid+1, r);
}
void Add(int x, int l, int r, int x1, int x2, int num) {
if (x1 == l && x2 == r) {
if (bz) tree[x].rp = min(tree[x].rp, num);
else tree[x].cp = max(tree[x].cp, num);
return;
}
int mid = (l+r)>>1;
if (x2 <= mid) Add(x*2, l, mid, x1, x2, num);
else if (x1 > mid) Add(x*2+1, mid+1, r, x1, x2, num);
else Add(x*2, l, mid, x1, mid, num), Add(x*2+1, mid+1, r, mid+1, x2, num);
}
void query(int x, int l, int r, int num) {
cmin = min(cmin, tree[x].rp);
cmax = max(cmax, tree[x].cp);
if (l == r) return;
int mid = (l+r)>>1;
if (num <= mid) query(x*2, l, mid, num);
else query(x*2+1, mid+1, r, num);
}
int main() {
int i;
scanf("%s", str+1);
Get_SA();
Get_height();
build(1, 1, n);
fo(i, 1, n) {
int x = sa[i], len = max(height[i], height[i+1]);
if (len == n-x+1) continue;
bz = 1; Add(1, 1, n, x, x+len, len+1);
if (x+len+1 <= n) bz = 0, Add(1, 1, n, x+len+1, n, x);
}
fo(i, 1, n) {
cmin = inf, cmax = -inf;
query(1, 1, n, i);
printf("%d\n", min(cmin, i-cmax+1));
}
return 0;
}