「NOI2016」优秀的拆分 - SAM

一类处理字符串相交的好东西。
题目就是要对每个位置求一整个位置为结尾/开头的能写成SS的串的数量。
除了一些麻烦的算法以外(比如在SAM上启发式合并之类的),一个简单的做法是枚举S的长度,不妨设为d,然后将原串每d个字符切开,发现SS这个串至少碰到两个缝隙,因此计算相邻两段的lcp/lcs就可以知道SS的结尾在某一段中合法的区间,然后用差分维护答案即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int N=30010;int A[N],B[N];char s[N];
namespace SAM_space{
	const int SIG=26,LOG=20,N=60010;
	struct SAM{
		int node_cnt,rt,las,val[N],fa[N],ch[N][SIG];vector<int> g[N];
		int f[N<<1][LOG],Log[N<<1],pos[N],d[N],fir[N],dfc;
		inline int new_node(int v) { int x=++node_cnt;val[x]=v,fa[x]=0;memset(ch[x],0,sizeof(int)*SIG);return x; }
		inline int clear() { return node_cnt=0,rt=las=new_node(0); }
		inline int extend(int w,int ps)
		{
			int p=las,np=new_node(val[p]+1);
			while(p&&!ch[p][w]) ch[p][w]=np,p=fa[p];
			if(!p) fa[np]=rt;
			else{
				int q=ch[p][w],v=val[p]+1;
				if(val[q]==v) fa[np]=q;
				else{
					int nq=new_node(v);
					memcpy(ch[nq],ch[q],sizeof(int)*SIG);
					fa[nq]=fa[q],fa[q]=fa[np]=nq;
					while(p&&ch[p][w]==q) ch[p][w]=nq,p=fa[p];
				}
			}
			return pos[ps]=las=np,0;
		}
		inline int Mymin(int x,int y) { return d[x]<d[y]?x:y; }
		inline int dfs(int x)
		{
			f[fir[x]=++dfc][0]=x,d[x]=d[fa[x]]+1;
			Rep(i,g[x]) dfs(g[x][i]),f[++dfc][0]=x;return 0;
		}
		inline int build()
		{
			int n=node_cnt;rep(i,1,n) vector<int>().swap(g[i]);
			rep(i,2,n) g[fa[i]].pb(i);
			dfc=0,dfs(1);rep(i,2,dfc) Log[i]=Log[i>>1]+1;
			for(int j=1;(1<<j)<=dfc;j++)
				for(int i=1;i+(1<<j)-1<=dfc;i++)
					f[i][j]=Mymin(f[i][j-1],f[i+(1<<(j-1))][j-1]);
			return 0;
		}
		inline int getLCA(int x,int y)
		{
			x=fir[x],y=fir[y];if(x>y) swap(x,y);
			int k=Log[y-x+1];return Mymin(f[x][k],f[y-(1<<k)+1][k]);
		}
		inline int query(int x,int y) { return val[getLCA(pos[x],pos[y])]; }
	}lcs,lcp;
}using SAM_space::lcs;using SAM_space::lcp;
inline int upd(int *A,int l,int r) { return A[l]++,A[r+1]--; }
inline int solve(int l,int n)
{
	for(int i=2;i<=n/l;i++)
	{
		int x=(i*l+1<=n?lcp.query((i-1)*l+1,i*l+1):0);
		int y=lcs.query((i-1)*l,i*l),L=max(l-y,0),R=min(x,l-1);
		if(L<=R) upd(A,i*l+L,i*l+R),upd(B,(i-2)*l+1+L,(i-2)*l+1+R);
	}
	return 0;
}
int main()
{
	for(int T=inn();T;T--)
	{
		scanf("%s",s+1);int n=(int)strlen(s+1);lint ans=0;
		lcs.clear(),lcp.clear();
		rep(i,1,n) lcs.extend(s[i]-'a',i);
		for(int i=n;i;i--) lcp.extend(s[i]-'a',i);
		lcs.build(),lcp.build();
		memset(A,0,sizeof(int)*(n+1));
		memset(B,0,sizeof(int)*(n+1));
		rep(len,1,n/2) solve(len,n);
		rep(i,1,n) A[i]+=A[i-1],B[i]+=B[i-1];
		rep(i,1,n-1) ans+=(lint)A[i]*B[i+1];
		printf("%lld\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值