【TJOI2017】DNA(后缀数组)

传送门


我去看了一下其他的题解,目前找得到的SAM上dfs和DP的做法全部复杂度都是假的。没什么价值。

哈希的做法复杂度带一个log,因为不满,而且出题人没有卡自然溢出,所以常数比后缀数组的做法小,十分优秀。

后缀数组的做法:将两个串拼接在一起构造后缀数组,于是我们现在可以支持 O ( 1 ) O(1) O(1)查询LCP,由于允许失配三次,我们利用LCP直接跳过相同的极长段即可。

可能SA-IS的后缀数组能够跑过哈希,不想写了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

cs int N=2e5+7;

char s[N];int n;
int sa[N],rk[N],ht[N];
inline void radix_sort(int *x,int *y,int m,int n){
	static int bin[N];
	memset(bin+1,0,sizeof(int)*m);
	for(int re i=1;i<=n;++i)++bin[x[i]];
	for(int re i=1;i<=m;++i)bin[i]+=bin[i-1];
	for(int re i=n;i;--i)sa[bin[x[y[i]]]--]=y[i];
}
inline void build_sa(){
	n=strlen(s+1);int m=128,*x=rk,*y=ht;
	for(int re i=1;i<=n;++i)x[i]=s[i],y[i]=i;
	radix_sort(x,y,m,n);
	for(int re i=1,ct=0;ct<n;i<<=1){
		ct=0;for(int re j=n-i+1;j<=n;++j)y[++ct]=j;
		for(int re j=1;j<=n;++j)if(sa[j]>i)y[++ct]=sa[j]-i;
		radix_sort(x,y,m,n);std::swap(x,y);x[sa[1]]=ct=1;
		for(int re j=2;j<=n;++j)
		x[sa[j]]=(y[sa[j]]==y[sa[j-1]]&&y[sa[j]+i]==y[sa[j-1]+i])?ct:++ct;
		m=ct;
	}
	for(int re i=1;i<=n;++i)rk[sa[i]]=i;
	for(int re i=1,k=0,j;i<=n;ht[rk[i++]]=k)
	for(k?--k:0,j=sa[rk[i]-1];s[i+k]==s[j+k];++k);
}

int mn[20][N],Log[N];
inline void init_st(){
	Log[0]=-1;
	for(int re i=1;i<=n;++i)Log[i]=Log[i>>1]+1,mn[0][i]=ht[i];
	for(int re i=1;(1<<i)<=n;++i)
	for(int re j=1;j+(1<<i)-1<=n;++j)
	mn[i][j]=std::min(mn[i-1][j],mn[i-1][j+(1<<i-1)]);
}
inline int lcp(int a,int b){
	a=rk[a],b=rk[b];
	if(a>b)std::swap(a,b);int t=Log[b-a];
	return std::min(mn[t][a+1],mn[t][b-(1<<t)+1]);
}

inline bool check(int l1,int l2,int p1){
	int p2=l1+1,l=lcp(p1,p2);
	if(p2+l>l2)return true;
	p1+=l+1,p2+=l+1,l=lcp(p1,p2);
	if(p2+l>l2)return true;
	p1+=l+1,p2+=l+1,l=lcp(p1,p2);
	if(p2+l>l2)return true;
	p1+=l+1,p2+=l+1,l=lcp(p1,p2);
	if(p2+l>l2)return true;
	return false;
}

inline void solve(){
	scanf("%s",s+1);int l1=strlen(s+1);
	scanf("%s",s+l1+1);int l2=strlen(s+l1+1)+l1;
	s[l2+1]='\0';build_sa();init_st();int ans=0;
	for(int re i=1,li=l1*2-l2+1;i<=li;++i)if(check(l1,l2,i))++ans;
	std::cout<<ans<<"\n";
}

signed main(){
#ifdef zxyoi
	freopen("DNA.in","r",stdin);
#endif
	int T;scanf("%d",&T);
	while(T--)solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值