zoukankan      html  css  js  c++  java
  • luoguP4770 [NOI2018]你的名字

    题意

    不妨先考虑(l=1,r=|S|)的情况:
    这时我们要求的其实是(S,T)的本质不同的公共子串数量。

    首先对(S)建一个后缀自动机,同时对于每个(T),我们也建一个自动机。

    根据后缀自动机的性质,后缀自动机的所有节点的代表的字符串的集合代表了(T)的全部子串,因此我们可以考虑(T)后缀自动机的上的每一个点代表的字符串中有多少是和(S)的公共子串。

    于是考虑怎么求这个东西:
    我们对于(T)的每一个前缀(T[1...i])求出(match_i)表示这个前缀的后缀能跟(S)匹配的最长长度,这个东西可以在(S)的后缀自动机上匹配(T)来求出,见这题

    对于一个节点(x),我们设它的字符串长度范围为([minlen_x,maxlen_x])(endpos)集合的第一个为(firpos_x)

    这时我们发现这个节点长度在(match_{firpos_x}+1)往上的字符串都不可能和(S)匹配,因此这个节点的贡献为(max(len_x-max(len_{fa_x},match_{firpos_x}),0))

    现在考虑([l,r])的限制,我们按照套路用线段树维护(S)的后缀自动机每个节点(endpos)集合,求(match)的时候只走合法的点即可。

    求match时注意个细节,写在注释里了。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6+10;
    int n,m,Q,tot,cnt_edge;
    int head[maxn<<1],root[maxn<<1],match[maxn];
    char s[maxn];
    struct edge{int to,nxt;}e[maxn<<2];
    struct Seg
    {
    	#define lc(p) (seg[p].lc)
    	#define rc(p) (seg[p].rc)
    	#define sum(p) (seg[p].sum)
    	int lc,rc,sum;
    }seg[maxn*50];
    inline void add_edge(int u,int v)
    {
    	e[++cnt_edge].nxt=head[u];
    	head[u]=cnt_edge;
    	e[cnt_edge].to=v;
    }
    void insert(int &p,int l,int r,int pos)
    {
    	if(!p)p=++tot;
    	sum(p)++;
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	if(pos<=mid)insert(lc(p),l,mid,pos);
    	else insert(rc(p),mid+1,r,pos);
    }
    int merge(int p,int q,int l,int r)
    {
    	if(!p||!q)return p+q;
    	int x=++tot;sum(x)=sum(p)+sum(q);
    	if(l==r)return x;
    	int mid=(l+r)>>1;
    	lc(x)=merge(lc(p),lc(q),l,mid);
    	rc(x)=merge(rc(p),rc(q),mid+1,r);
    	return x;
    }
    int query(int p,int l,int r,int ql,int qr)
    {
    	if(!p)return 0;
    	if(l>=ql&&r<=qr)return sum(p);
    	int mid=(l+r)>>1,res=0;
    	if(ql<=mid)res+=query(lc(p),l,mid,ql,qr);
    	if(qr>mid)res+=query(rc(p),mid+1,r,ql,qr);
    	return res;
    }
    void dfs(int x)
    {
    	for(int i=head[x];i;i=e[i].nxt)
    		dfs(e[i].to),root[x]=merge(root[x],root[e[i].to],1,n);
    }
    struct SAM
    {
    	int tot,last;
    	int fa[maxn<<1],len[maxn<<1],firpos[maxn<<1];
    	int ch[maxn<<1][30];
    	inline void clear()
    	{
    		for(int i=1;i<=tot;i++)
    		{
    			fa[i]=len[i]=firpos[i]=0;
    			memset(ch[i],0,sizeof(ch[i]));
    		}
    		last=tot=1;
    	}
    	inline void add(int c,int id)
    	{
    		int now=++tot;len[now]=len[last]+1;firpos[now]=id;
    		int p=last;last=now;
    		while(p&&!ch[p][c])ch[p][c]=now,p=fa[p];
    		if(!p){fa[now]=1;return;}
    		int q=ch[p][c];
    		if(len[q]==len[p]+1)fa[now]=q;
    		else 
    		{
    			int nowq=++tot;len[nowq]=len[p]+1;firpos[nowq]=firpos[q];
    			memcpy(ch[nowq],ch[q],sizeof(ch[q]));
    			fa[nowq]=fa[q];fa[q]=fa[now]=nowq;
    			while(p&&ch[p][c]==q)ch[p][c]=nowq,p=fa[p];
    		}
    	}
    }sam1,sam2; 
    inline int read()
    {
    	char c=getchar();int res=0,f=1;
    	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
    	return res*f;
    }
    inline void getmatch(char* s,int l,int r)
    {
    	int len=strlen(s+1),now=1,nowl=0;
    	for(int i=1;i<=len;i++)
    	{
    		while(2333)
    		{
    			if(sam1.ch[now][s[i]-'a']&&query(root[sam1.ch[now][s[i]-'a']],1,n,l+nowl,r))//注意是l+nowl,这是endpos的集合。
    			{
    				now=sam1.ch[now][s[i]-'a'],nowl++;
    				break;
    			}
    			if(!nowl)break;
    			nowl--;//注意不要直接跳fa[now],因为区间缩小可能产生答案。
    			if(nowl==sam1.len[sam1.fa[now]])now=sam1.fa[now];
    		}
    		match[i]=nowl;
    	}
    }
    int main()
    {
    	scanf("%s",s+1);n=strlen(s+1);
    	sam1.clear();
    	for(int i=1;i<=n;i++)sam1.add(s[i]-'a',i),insert(root[sam1.last],1,n,i);
    	for(int i=2;i<=sam1.tot;i++)add_edge(sam1.fa[i],i);
    	dfs(1);
    	Q=read();
    	while(Q--)
    	{
    		ll ans=0;
    		scanf("%s",s+1);m=strlen(s+1);
    		int l=read(),r=read();
    		sam2.clear();
    		for(int i=1;i<=m;i++)sam2.add(s[i]-'a',i);
    		getmatch(s,l,r);
    		for(int i=2;i<=sam2.tot;i++)
    			ans+=max(0,sam2.len[i]-max(match[sam2.firpos[i]],sam2.len[sam2.fa[i]]));
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    vim跳转(一)
    代理服务器
    python类
    python 删除/查找重复项
    DNS缓存
    DNS查询过程
    http返回头中content-length与Transfer-Encoding: chunked的问题释疑
    jquery mouseover与mouseenter,mouserout与mouseleave的区别
    php提示Call-time pass-by-reference has been deprecated in的解决方法
    uboot启动内核(3)
  • 原文地址:https://www.cnblogs.com/nofind/p/12063358.html
Copyright © 2011-2022 走看看