题解 [LNOI2022] 串
这题要是放 T1 大概能过一车吧……
发现序列是 \([l, r]\to [l+1, r+2]\to\cdots\)
考虑倒推,发现有个事情是若当前是某个在 \(s\) 中出现了至少两次的串的子串,则可以跳到另一次出现中
那么我们总是可以从靠左的那次出现跳到靠右的那次出现,循环往复直到串长为 0
所以出现过多次的串(在最靠左的一次统计)贡献就是对 \(r-l+1+\lfloor\frac{n-r}{2}\rfloor\) 取 max
考虑不进行跳跃,答案要对 \(\lfloor\frac{n}{2}\rfloor\) 取 max
使用 SAM 即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define fir first
#define sec second
#define ll long long
#define ull unsigned long long
//#define int long long
int n;
char s[N];
namespace force{
ull h[N], pw[N];
const ull base=13131;
unordered_map<ull, int> dp;
inline void chkmax(int& a, int b) {a=max(a, b);}
inline ull hashing(int l, int r) {return h[r]-h[l-1]*pw[r-l+1];}
void solve() {
pw[0]=1; dp.clear();
for (int i=1; i<=n; ++i) pw[i]=pw[i-1]*base;
for (int i=1; i<=n; ++i) h[i]=h[i-1]*base+s[i];
dp[0]=0;
for (int len=2; len<=n; ++len) {
for (int l=1; l+len-1<=n; ++l) {
int r=l+len-1;
chkmax(dp[hashing(l+1, r)], dp[hashing(l, r-2)]+1);
}
}
int ans=0;
for (auto it:dp) ans=max(ans, it.sec);
printf("%d\n", ans);
}
}
namespace task{
int len[N], fail[N], endpos[N], tr[N][26], siz[N], tem[N], cnt[N], now, tot, ans;
void init() {fail[now=tot=0]=-1; memset(tr[0], 0, sizeof(tr[0]));}
void ins(char c) {
c-='a';
int cur=++tot;
len[cur]=len[now]+1;
endpos[cur]=INF, siz[cur]=0;
for (int i=0; i<26; ++i) tr[cur][i]=0;
int p, q;
for (p=now; ~p&&!tr[p][c]; tr[p][c]=cur,p=fail[p]);
if (p==-1) fail[cur]=0;
else if (len[q=tr[p][c]]==len[p]+1) fail[cur]=q;
else {
int cln=++tot;
endpos[cln]=INF, siz[cln]=0;
len[cln]=len[p]+1;
fail[cln]=fail[q];
for (int i=0; i<26; ++i) tr[cln][i]=tr[q][i];
for (; ~p&&tr[p][c]==q; tr[p][c]=cln,p=fail[p]);
fail[cur]=fail[q]=cln;
}
endpos[now=cur]=len[cur];
siz[cur]=1;
}
void solve() {
init();
for (int i=1; i<=n; ++i) ins(s[i]);
for (int i=1; i<=n; ++i) cnt[i]=0;
for (int i=1; i<=tot; ++i) ++cnt[len[i]];
for (int i=1; i<=n; ++i) cnt[i]+=cnt[i-1];
for (int i=1; i<=tot; ++i) tem[cnt[len[i]]--]=i;
for (int i=tot; i; --i) {
endpos[fail[tem[i]]]=min(endpos[fail[tem[i]]], endpos[tem[i]]);
siz[fail[tem[i]]]+=siz[tem[i]];
}
ans=n/2;
for (int i=1; i<=tot; ++i) if (siz[i]>1) ans=max(ans, len[i]+(n-endpos[i])/2);
printf("%d\n", ans);
}
}
signed main()
{
// freopen("string.in", "r", stdin);
// freopen("string.out", "w", stdout);
int T;
scanf("%d", &T);
while (T--) {
scanf("%s", s+1);
n=strlen(s+1);
// if (n<=1000) force::solve();
// else printf("%d\n", (n/2)+1);
task::solve();
}
return 0;
}