题目
题目描述
给定一个字符串 S=S1,S2,S3,⋯,SnS=S1,S2,S3,⋯,Sn, 对于每一个 1≤i≤n1≤i≤n, 求
f(i)=max{1≤j≤i|∃k,j≤k<i−j+1∧S[k−j+1,k]=S[i−j+1,i]}
f(i)=max{1≤j≤i|∃k,j≤k<i−j+1∧S[k−j+1,k]=S[i−j+1,i]}
其中 S[a,b]S[a,b] 表示 SS 以 aa 开始, bb结尾的长度为 b−a+1b−a+1 的子串. 对空集取 maxmax 的结果为 00.
换句话说, 就是对每个 ii, 求一个最大的 jj, 使得 S[i−j+1,i]S[i−j+1,i] 是 S[1…i−j]S[1…i−j] 的子串.
输入格式
输入只有一行, 包括一个字符串 SS, 仅包含字符 0 和 1.
输出格式
输出 |S||S| 行, 每行包括一个整数, 表示答案.
样例 1
输入
100101010111001
输出
0
0
1
1
2
2
2
2
3
4
1
1
2
3
4
样例 2
输入
0000001111111010101010101
输出
0
1
1
2
2
3
0
1
1
2
2
3
3
1
2
2
2
2
3
4
4
4
5
6
6
数据范围
测试点 1,21,2: 1≤|S|≤6001≤|S|≤600
测试点 3,4,53,4,5: 1≤|S|≤30001≤|S|≤3000
测试点 6,7,8,9,10,116,7,8,9,10,11: 1≤|S|≤1051≤|S|≤105
测试点 12,13,1412,13,14: 1≤|S|≤4×1051≤|S|≤4×105
测试点 15,16,1715,16,17: 1≤|S|≤7×1051≤|S|≤7×105
测试点 18,19,20,21,22,23,24,2518,19,20,21,22,23,24,25: 1≤|S|≤106
思路
关注以下两个事实.
-
如果 s [ i − j + 1 , i ] s[i - j + 1, i] s[i−j+1,i] 是 s [ 1 , i − j ] s[1, i - j] s[1,i−j] 的子串, 那么对于所有的 j ′ ≤ j j^{'} \le j j′≤j, 都有 s [ i − j ′ + 1 , i ] s[i - j^{'} + 1, i] s[i−j′+1,i] 是 s [ 1 , i − j ′ ] s[1, i - j^{'}] s[1,i−j′] 的子串.
-
如果 s [ i − j + 1 , i ] s[i - j + 1, i] s[i−j+1,i] 是 s [ 1 , i − j ] s[1, i - j] s[1,i−j] 的子串, 那么 s [ i − j + 1 , i − 1 ] s[i - j + 1, i - 1] s[i−j+1,i−1] 也是 s [ 1 , i − j − 1 ] s[1, i - j - 1] s[1,i−j−1] 的子串.
假设我们已经知道了 f ( i ) f(i) f(i), 即知道了 s [ i − f ( i ) + 1 , i ] s[i - f(i) + 1, i] s[i−f(i)+1,i] 是 f [ 1 , i − f ( i ) ] f[1, i - f(i)] f[1,i−f(i)] 的子串, 接下来我们需要算 f ( i + 1 ) f(i + 1) f(i+1), 那么显然 f ( i + 1 ) f(i + 1) f(i+1) 最长只能是 f ( i ) + 1 f(i) + 1 f(i)+1.
我们就需要判断 s [ ( i + 1 ) − ( f ( i ) + 1 ) + 1 , ( i + 1 ) ] s[(i + 1) - (f(i) + 1) + 1, (i + 1)] s[(i+1)−(f(i)+1)+1,(i+1)] 是否是 s [ 1 , ( i + 1 ) − ( f ( i ) + 1 ) ] s[1, (i + 1) - (f(i) + 1)] s[1,(i+1)−(f(i)+1)] 的子串. 如果我们知道 s [ ( i + 1 ) − ( f ( i ) + 1 ) + 1 , ( i + 1 ) ] s[(i + 1) - (f(i) + 1) + 1, (i + 1)] s[(i+1)−(f(i)+1)+1,(i+1)] 是 SAM 上的哪个节点, 我们可以根据 right 集合简单的判断(只要知道这个节点right集合里面的最小值, 就能知道 s [ ( i + 1 ) − ( f ( i ) + 1 ) + 1 , ( i + 1 ) ] s[(i + 1) - (f(i) + 1) + 1, (i + 1)] s[(i+1)−(f(i)+1)+1,(i+1)] 在整个串中的第一次出现的位置. )
设 s [ i − f ( i ) + 1 , i ] s[i - f(i) + 1, i] s[i−f(i)+1,i] 对应节点为 u u u, s [ ( i + 1 ) − ( f ( i ) + 1 ) + 1 , ( i + 1 ) ] s[(i + 1) - (f(i) + 1) + 1, (i + 1)] s[(i+1)−(f(i)+1)+1,(i+1)] 对应节点为 v v v, 显然有 v = g o t o ( u , s [ i + 1 ] ) v = goto(u, s[i + 1]) v=goto(u,s[i+1]).
如果 s [ ( i + 1 ) − ( f ( i ) + 1 ) + 1 , ( i + 1 ) ] s[(i + 1) - (f(i) + 1) + 1, (i + 1)] s[(i+1)−(f(i)+1)+1,(i+1)] 不是 s [ 1 , ( i + 1 ) − ( f ( i ) + 1 ) ] s[1, (i + 1) - (f(i) + 1)] s[1,(i+1)−(f(i)+1)] 的子串, 那么我们需要考虑 s [ ( i + 1 ) − ( f ( i ) + 1 ) + 1 , ( i + 1 ) ] s[(i + 1) - (f(i) + 1) + 1, (i + 1)] s[(i+1)−(f(i)+1)+1,(i+1)] 的后缀, 这些串对应位置是 v v v 的祖先, 暴力往根方向找即可.
不考虑 SAM 的建树时间, 求答案的复杂度是 Θ ( n ) \Theta(n) Θ(n) 的.
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1000000 + 7;
const int si = 2;
struct SAM {
int tot,last;
int son[maxn * 2][si],maxlen[maxn * 2],pre[maxn * 2];
int fi[maxn * 2];
SAM() { last = tot = 1; }
void init() { tot = 0; last = newn(); }
int newn() {
++tot;
memset(son[tot],0,sizeof(son[tot]));
fi[tot] = -1;
maxlen[tot] = pre[tot] = 0;
return tot;
}
void add(int c,int idx) {
int now = newn();
maxlen[now] = maxlen[last] + 1;
fi[now] = idx;
while (last && son[last][c] == 0)
son[last][c] = now,last = pre[last];
if (last) {
int x = son[last][c];
if (maxlen[x] == maxlen[last] + 1) pre[now] = x;
else {
int nq = newn();
maxlen[nq] = maxlen[last] + 1;
memcpy(son[nq],son[x],sizeof(son[x]));
fi[nq] = fi[x];
pre[nq] = pre[x];
pre[x] = pre[now] = nq;
while (last && son[last][c] == x)
son[last][c] = nq,last = pre[last];
}
}
else pre[now] = 1;
last = now;
}
} sam;
char s[maxn];
int main(int argc,char **argv) {
scanf("%s",s);
int n = strlen(s);
int u = 1;
for (int i = 0; i < n; i++) {
sam.add(s[i] - '0',i);
u = sam.son[u][s[i] - '0'];
while (u != 1 && sam.maxlen[ sam.pre[u] ] + 1 + sam.fi[u] > i) u = sam.pre[u];
if (sam.maxlen[u] + sam.fi[u] <= i) {
printf("%d\n",sam.maxlen[u]);
}
else {
printf("%d\n",i - sam.fi[u]);
}
}
return 0;
}