BZOJ4566: [Haoi2016]找相同字符

4566: [Haoi2016]找相同字符
对第一个串建出后缀自动机,统计出每个节点对答案的贡献,再用第二个串跑一遍统计答案。

//# pragma GCC optimize ("O2")
//# pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
#define lb(x) ((x)&(-x))
#define out(i,a,now) for(int i=a.be[now],to=a.v[i];~i;i=a.ne[i],to=a.v[i])
#define fo(i,a,b) for(i=(a);i<=(b);++i)
#define fd(i,a,b) for(i=(a);i>=(b);--i)
#define mid ((l+r)>>1)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a>b?b:a)
#define mp(a,b) make_pair(a,b)
#define qk(x) memset(x,0,sizeof(x))
#define RG register
#define SF scanf
#define PF printf
#define FP freopen
#define gs(x) (tr[tr[x].f][1]==x)
#define cpy(a,b) memcpy(a,b,sizeof(a))
#ifdef Mogician
#endif
using namespace std;
typedef long long LL;
typedef double DB;
typedef pair<LL,LL> Pair;
template <class T> void cmax(T &a,T b)
{
    a=max(a,b);
}
template <class T> void cmin(T &a,T b)
{
    a=min(a,b);
}
const LL maxn=201000,alpha=26;
struct data
{
	LL nxt[alpha+2],ma,f,sum,cnt;
	data()
	{
		ma=f=sum=cnt=0;
		qk(nxt);
	}
	LL& operator [](const LL& b)
	{
		return nxt[b];
	}
}tr[maxn*2];
struct Suffix_Automaton
{
	LL cnt,root,last;
	LL new_node(LL ma)
	{
		tr[++cnt]=data();
		tr[cnt].ma=ma;
		return cnt;
	}
	Suffix_Automaton()
	{
		cnt=0;
		root=last=new_node(0);
	}
	void add(LL x)
	{
		LL p=last,np=new_node(tr[p].ma+1);
		last=np; tr[np].cnt=1;
		for(;p&&!tr[p][x];p=tr[p].f) tr[p][x]=np;
		if (!p) tr[np].f=root;
		else
		{
			LL q=tr[p][x];
			if (tr[q].ma==tr[p].ma+1) tr[np].f=q;
			else
			{
				LL nq=new_node(tr[p].ma+1);
				cpy(tr[nq].nxt,tr[q].nxt);
				tr[nq].f=tr[q].f; tr[q].f=nq; tr[np].f=nq;
				for(;p&&tr[p][x]==q;p=tr[p].f) tr[p][x]=nq;
			}
		}
	}
}sam;
LL n,a[maxn*2],flag;
char s[maxn];
bool cmp(const LL& a,const LL& b)
{
	return tr[a].ma*flag>tr[b].ma*flag;
}
int main()
{
#ifdef Mogician
	FP("bzoj4566.in","r",stdin);
	FP("bzoj4566.out","w",stdout);
#endif 
	SF("%s",s);
	n=strlen(s);
	LL i;
	fo(i,0,n-1) sam.add(s[i]-'a');
	fo(i,1,sam.cnt) a[i]=i;
	flag=1;
	sort(a+1,a+sam.cnt+1,cmp);
	fo(i,1,sam.cnt) tr[tr[a[i]].f].cnt+=tr[a[i]].cnt;
	flag=-1;
	sort(a+1,a+sam.cnt+1,cmp);
	LL ans=0;
	fo(i,2,sam.cnt)
	{
		tr[a[i]].sum+=tr[tr[a[i]].f].sum+(tr[a[i]].ma-tr[tr[a[i]].f].ma)*tr[a[i]].cnt;
	}
	SF("%s",s);
	n=strlen(s);
	LL now=sam.root,len=0;
	//维护最长匹配长度len的方法:
	fo(i,0,n-1)
	{
		if (!tr[now][s[i]-'a'])
		{
			while (now&&!tr[now][s[i]-'a']) now=tr[now].f;
			if (!now) now=sam.root,len=0;
			else
			{
				len=tr[now].ma+1;
				now=tr[now][s[i]-'a'];
			}
		}
		else
		{
			now=tr[now][s[i]-'a'];
			++len;
		}
		assert(tr[tr[now].f].ma<len);
		ans+=tr[now].sum-(tr[now].ma-len)*tr[now].cnt;//减去不足ma的部分
	}
	PF("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值