[SDOI2008] Sandy的卡片 题解

讲一种自认为最暴力的方法。

首先肯定还是用差分的思想,对于每一张卡片进行重新标号,在卡片串与卡片串中插入特殊字符,然后找重复了 \(n\) 次的子串。

这里我们对于每一个子串开一个大小为 \(n\) 的标记,表示他在不在第 \(i\) 个卡片串里。

然而这样很明显容易 \(\text{MLE+TLE}\),考虑优化。

发现用 \(bitset\) 可以直接解决这个问题,时空复杂度 \(O(\dfrac{n^2m}{32})\),可以通过。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+5;
int n,ans,len;
struct SAM{
	bitset<1005>sm[N];int ln[N],sz[N];
	int id,tl,pr[N];map<int,int>tr[N];
	vector<int>g[N];int cnt[N];
	SAM(){pr[0]=-1;}
	void cpy(int x,int y){
		tr[x]=tr[y];
		pr[x]=pr[y];ln[x]=ln[y];
	}void add(int x,int y){
		ln[++id]=ln[tl]+1;
		int p=tl;tl=id;
		while(~p&&!tr[p][x])
			tr[p][x]=id,p=pr[p];
		if(p<0){
			sm[tl][y]=1;
			return;
		}int u=p,v=tr[p][x];
		if(ln[u]+1==ln[v]){
			pr[id]=v;
			sm[tl][y]=1;
			return;
		}cpy(++id,v);
		ln[id]=ln[u]+1;
		pr[v]=pr[id-1]=id;
		while(~p&&tr[p][x]==v)
			tr[p][x]=id,p=pr[p];
		sm[tl][y]=1;
	}void dfs(int x){
		for(int i=0;i<g[x].size();i++){
			dfs(g[x][i]);
			sm[x]|=sm[g[x][i]];
		}int flag=0;
		for(int j=1;j<=n;j++)
			flag+=sm[x][j];
		if(flag==n) ans=max(ans,ln[x]);
	}void jb(){
		for(int i=1;i<=id;i++)
			g[pr[i]].push_back(i);
	}
}sam;int m,s[N];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>m;sam.add(10000+i,i);
		for(int j=1;j<=m;j++)
			cin>>s[j];
		for(int j=2;j<=m;j++)
			sam.add(s[j]-s[j-1]+3728,i);
	}sam.jb();sam.dfs(0);
	cout<<ans+1;
	return 0;
}//man!what can I say!
posted @ 2024-07-10 09:33  长安一片月_22  阅读(6)  评论(0编辑  收藏  举报