题解 [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;
}
posted @ 2022-06-08 09:01  Administrator-09  阅读(1)  评论(0编辑  收藏  举报