题目描述
为了让你更好地理解题面,给出若干关于字符串的定义:
- 对于一个字符串 S = s_1 s_2 \cdots s_nS=s1s2⋯sn,定义其长度为 \lvert S \rvert = n∣S∣=n。
- 对于两个字符串 S = s_1 s_2 \cdots s_nS=s1s2⋯sn 和 T = t_1 t_2 \cdots t_mT=t1t2⋯tm,称 TT 为 SS 的子串,若 m = 0m=0(即 TT 为空串)或者 \exists 1 \le i \le j \le n∃1≤i≤j≤n,T = s_i s_{i + 1} \cdots s_jT=sisi+1⋯sj。若 m = 0m=0 或上述判断条件中 ii 可以取到 11,则称 TT 为 SS 的前缀;若 m = 0m=0 或上述判断条件中 jj 可以取到 nn,则称 TT 为 SS 的后缀。
给定一个英文小写字母构成的字符串 SS,你需要找到一个尽可能长的字符串序列 (T_0, T_1, \ldots, T_l)(T0,T1,…,Tl),满足:
- T_0T0 是 SS 的子串;
- \forall 1 \le i \le l∀1≤i≤l,\lvert T_i \rvert - \lvert T_{i - 1} \rvert = 1∣Ti∣−∣Ti−1∣=1;
- \forall 1 \le i \le l∀1≤i≤l,存在 SS 的一个长度为 \lvert T_i \rvert + 1∣Ti∣+1 的子串 S'_iSi′,使得 S'_iSi′ 的长度为 \lvert T_{i - 1} \rvert∣Ti−1∣ 的前缀为 T_{i - 1}Ti−1,长度为 \lvert T_i \rvert∣Ti∣ 的后缀为 T_iTi。
输出这样的字符串序列的长度的最大值(即 ll 的最大值)。
输入格式
本题有多组测试数据。输入的第一行为一个整数 TT,表示测试数据组数。对于每组测试数据,输入一行一个英文小写字母构成的字符串 SS。
输出格式
对于每组测试数据输出一行一个整数,表示题目描述中字符串序列长度的最大值。
输入输出样例
输入 #1 输出 #1
3 2
abcd 3
abab 0
a
下文中使用符号 \epsilonϵ 表示空串。
对于第一组测试数据,可以找到如下字符串序列:T_0 = \epsilon, T_1 = \texttt{b}, T_2 = \texttt{cd}T0=ϵ,T1=b,T2=cd,其中 S'_1 = \texttt{ab}, S'_2 = \texttt{bcd}S1′=ab,S2′=bcd。
对于第二组测试数据,可以找到如下字符串序列:T_0 = \epsilon, T_1 = \texttt{b}, T_2 = \texttt{ab}, T_3 = \texttt{bab}T0=ϵ,T1=b,T2=ab,T3=bab,其中 S'_1 = \texttt{ab}, S'_2 = \texttt{bab}, S'_3 = \texttt{abab}S1′=ab,S2′=bab,S3′=abab。
对于第三组测试数据,可以找到如下字符串序列:T_0 = \epsilonT0=ϵ。
【样例 #2】
见附件中的 string/string2.in
与 string/string2.ans
。
该组样例中的字符串长度有一定梯度,你可以利用该组样例对程序进行检查。
【样例 #3】
见附件中的 string/string3.in
与 string/string3.ans
。
该组样例满足特殊性质 A。
【数据范围】
设 \sum |S|∑∣S∣ 表示测试点中所有测试数据的字符串长度和。
对于 100 \%100% 的测试数据,T \ge 1T≥1,1 \le \lvert S \rvert \le 5 \times {10}^51≤∣S∣≤5×105,1 \le \sum \lvert S \rvert \le 1.5 \times {10}^61≤∑∣S∣≤1.5×106。
测试点编号 | \lvert S \rvert \le∣S∣≤ | \sum \lvert S \rvert \le∑∣S∣≤ | 特殊性质 |
---|---|---|---|
1 \sim 21∼2 | 3030 | 150150 | 无 |
3 \sim 53∼5 | 200200 | 800800 | 无 |
6 \sim 86∼8 | 10001000 | 30003000 | 无 |
9 \sim 119∼11 | 5 \times {10}^55×105 | 1.5 \times {10}^61.5×106 | A |
12 \sim 1512∼15 | 6 \times {10}^46×104 | 3 \times {10}^53×105 | 无 |
16 \sim 2016∼20 | 5 \times {10}^55×105 | 1.5 \times {10}^61.5×106 | 无 |
特殊性质 A:字符串中的每个字符在小写字母中独立均匀随机生成。
【提示】
本题输入输出量较大,请使用较为快速的输入输出方式。
例如,若你的代码使用了 cin
和 cout
作为输入输出方式,你可以选择在代码的输入输出重定向语句(freopen
语句、 fopen
语句等)之后加入以下语句加速输入输出速度。
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
Description
给定一个英文小写字母构成的字符串 SS,你需要找到一个尽可能长的字符串序列 (T_0, T_1, \ldots, T_l)(T0,T1,…,Tl),满足:
- T_0T0 是 SS 的子串;
- \forall 1 \le i \le l∀1≤i≤l,\lvert T_i \rvert - \lvert T_{i - 1} \rvert = 1∣Ti∣−∣Ti−1∣=1;
- \forall 1 \le i \le l∀1≤i≤l,存在 SS 的一个长度为 \lvert T_i \rvert + 1∣Ti∣+1 的子串 S_i'Si′,使得 S_i'Si′ 的长度为 \lvert T_{i - 1} \rvert∣Ti−1∣ 的前缀为 T_{i - 1}Ti−1,长度为 \lvert T_i \rvert∣Ti∣ 的后缀为 T_iTi。
输出这样的字符串序列的长度的最大值(即 ll 的最大值)。
Solution
一个思路很清奇的题目,乍一看以为和19年省选的那个字符串问题差不多,其实完全没关系。
观察一下这个字符串序列,逆向去想一下,可以这么构造:[i,j]\rightarrow [i-1,j-2]\rightarrow [i-2,j-4]...[i,j]→[i−1,j−2]→[i−2,j−4]... 一直这样下去直到长度为 00 或者到头了。那么答案就有一个显然的下界 \lfloor n/2\rfloor⌊n/2⌋。
然后上面这个构造的瓶颈在于走到头就没得走了,我们需要考虑什么时候可以往后跳回去。
然后就是比较重要的性质了:一个串可以往回跳的充要条件是出现了至少两次。
假设出现位置是 [i_1,j_1],[i_2,j_2](i_1<i_2)[i1,j1],[i2,j2](i1<i2),那么我们从 [i_2,j_2][i2,j2] 开始,按照上述的构造方法构造,当左端点到 i_1i1 时,我们跳回到 i_2i2 即可。
反过来的话就是如果只出现一次,那么这个串对应的 S_i'Si′ 是唯一的,别的跳不回来。
这样就很简单了,如果可以往回跳那么我们从 [i_2,j_2][i2,j2] 开始一路跳就可以跳到长度为 00。
对于一个出现至少两次的串 [l,r][l,r],若选择它作为跳的开始,答案为 r-l+1+\lfloor(n-r)/2\rfloorr−l+1+⌊(n−r)/2⌋。
我们求出以每个下标为右端点的最长的出现至少两次的串,取个 max。
这个事情用 SAM 搞一搞就可以做,就对于每个节点记录最靠左的位置和 siz 就好了。
Code:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
namespace solve
{
const int maxn = 2e6 + 10;
int n;
struct SAM
{
int mx[maxn], siz[maxn], ch[maxn][26], father[maxn], pos[maxn];
int lst, tot;
int ans;
void insert(int c, int id)
{
int p = ++tot, f = lst;
siz[p] = 1, mx[p] = mx[f] + 1, lst = p, pos[p] = id;
while (f && !ch[f][c])
ch[f][c] = p, f = father[f];
if (!f)
father[p] = 1;
else
{
int q = ch[f][c];
if (mx[q] == mx[f] + 1)
father[p] = q;
else
{
int nq = ++tot;
memcpy(ch[nq], ch[q], sizeof(ch[q])), father[nq] = father[q], mx[nq] = mx[f] + 1;
pos[nq] = n + 1;
father[p] = father[q] = nq;
while (f && ch[f][c] == q)
ch[f][c] = nq, f = father[f];
}
}
}
vector<int> e[maxn];
void build()
{
for (int i = 2; i <= tot; i++)
e[father[i]].push_back(i);
}
void dfs(int x)
{
for (int v : e[x])
{
dfs(v);
siz[x] += siz[v], pos[x] = min(pos[x], pos[v]);
}
if (siz[x] > 1)
ans = max(ans, (n - pos[x]) / 2 + mx[x]);
}
void clear()
{
for (int i = 1; i <= tot; i++)
e[i].clear(), memset(ch[i], 0, sizeof(ch[i]));
memset(father, 0, sizeof(int) * (tot + 4));
memset(mx, 0, sizeof(int) * (tot + 4));
memset(siz, 0, sizeof(int) * (tot + 4));
memset(pos, 0, sizeof(int) * (tot + 4));
lst = tot = 1;
ans = 0;
}
SAM() { lst = tot = 1; }
} sam;
char s[maxn];
void main()
{
cin >> s;
n = strlen(s);
for (int i = 0; i < n; i++)
sam.insert(s[i] - 'a', i + 1);
sam.build(), sam.dfs(1);
cout << max(sam.ans, n / 2) << endl;
sam.clear();
}
}
int main()
{
ios::sync_with_stdio(false);
int T;
cin >> T;
while (T--)
solve::main();
}