题目

题目描述
给定一个字符串 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; 
}