• NOI2018 你的名字


    你的名字

    题目背景

    实力强大的小A 被选为了ION2018 的出题人,现在他需要解决题目的命名问题。

    题目描述

    小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了。

    由于ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手册规定:每年由命题委员会规定一个小写字母字符串,我们称之为那一年的命名串,要求每道题的名字必须是那一年的命名串的一个非空连续子串,且不能和前一年的任何一道题目的名字相同。

    由于一些特殊的原因,小A 不知道ION2017 每道题的名字,但是他通过一些特殊手段得到了ION2017 的命名串,现在小A 有Q 次询问:每次给定ION2017 的命名串和ION2018 的命名串,求有几种题目的命名,使得这个名字一定满足命题委员会的规定,即是ION2018 的命名串的一个非空连续子串且一定不会和ION2017 的任何一道题目的名字相同。

    由于一些特殊原因,所有询问给出的ION2017 的命名串都是某个串的连续子串,详细可见输入格式。

    输入输出格式

    输入格式:

    第一行一个字符串S ,之后询问给出的ION2017 的命名串都是S 的连续子串。 第二行一个正整数Q,表示询问次数。 接下来Q 行,每行有一个字符串T 和两个正整数$l,r$,表示询问如果ION2017 的 命名串是$S [l..r]$,ION2018 的命名串是T 的话,有几种命名方式一定满足规定。

    输出格式:

    输出Q 行,第i 行一个非负整数表示第i 个询问的答案。

    输入输出样例

    输入样例#1: 复制
    scbamgepe
    3
    smape 2 7
    sbape 3 8
    sgepe 1 9
    输出样例#1: 复制
    12
    10
    4

    说明

    测试点 $|S|leq$ $Qleq $ $sum |T|leq $ 其他限制
    1 $200$ $200$ $40000$ $|T|leq 200$
    2 $1000$ $200$ $40000$ $|T|leq 200$
    3 $1000$ $200$ $40000$ $|T|leq 200$
    4 $1000$ $200$ $5 imes 10^5$
    5 $1000$ $200$ $5 imes 10^5$
    6 $5 imes 10^5$ $1$ $5 imes 10^5$
    7 $5 imes 10^5$ $1$ $5 imes 10^5$
    8 $10^5$ $10^5$ $2 imes 10^5$
    9 $10^5$ $10^5$ $2 imes 10^5$ 字符串随机
    10 $2 imes 10^5$ $10^5$ $4 imes 10^5$
    11 $2 imes 10^5$ $10^5$ $4 imes 10^5$ 字符串随机
    12 $3 imes 10^5$ $10^5$ $6 imes 10^5$
    13 $3 imes 10^5$ $10^5$ $6 imes 10^5$ 字符串随机
    14 $4 imes 10^5$ $10^5$ $8 imes 10^5$
    15 $4 imes 10^5$ $10^5$ $8 imes 10^5$ 字符串随机
    16 $5 imes 10^5$ $10^5$ $10^6$
    17 $5 imes 10^5$ $10^5$ $10^6$ 字符串随机
    18 $2 imes 10^5$ $10^5$ $10^6$
    19 $3 imes 10^5$ $10^5$ $10^6$
    20 $4 imes 10^5$ $10^5$ $10^6$
    21 $5 imes 10^5$ $10^5$ $10^6$
    22 $5 imes 10^5$ $10^5$ $10^6$
    23 $5 imes 10^5$ $10^5$ $10^6$
    24 $5 imes 10^5$ $10^5$ $10^6$
    25 $5 imes 10^5$ $10^5$ $10^6$

    对于前17个测试点的所有询问有$l=1,r=|S|$

    对于所有数据,保证 $1leq l leq r leq |S|$,$1leq |T|leq 5 imes 10^5$

    题解

    补集转换一步,问题变成 ((T) 的本质不同的子串数) - ((T)(S[l_i:r_i]) 的本质不同的公共子串数)。

    第一个问题是 SAM 模板。

    针对第二个问题,我们要找到 (T) 的 SAM 上的每个节点 (x)(T) 中合法长度中 ([len_{fa_x}+1,len_x]) 和 在 (S) 中的合法长度的交集。那么我们便要去找每个节点 (x) 能匹配到 (S[l_i:r_i]) 中某个串时的最大后缀长度 (res_x)

    (len_x) 可以把 (T) 丢到 (S) 的 SAM 上去匹配。对于每个 (T) 的前缀找到在 (S) 中的最长匹配位置,然后不断删去首字母,直到能在 (S[l_i:r_i]) 放下。这个用线段树合并维护 right 集合即可。

    时间复杂度(O((|S|+sum|T|)log |S|))

    co int N=2e6;
    char s[N];
    int res[N],n,q;
    // Interval Tree
    namespace T{
    	int siz[N*25],lc[N*25],rc[N*25],tot;
    	void insert(int&x,int l,int r,int p){
    		if(!x) x=++tot;
    		++siz[x];
    		if(l==r) return;
    		int mid=l+r>>1;
    		if(p<=mid) insert(lc[x],l,mid,p);
    		else insert(rc[x],mid+1,r,p);
    	}
    	int merge(int x,int y){
    		if(!x||!y) return x+y;
    		int o=++tot;
    		siz[o]=siz[x]+siz[y];
    		lc[o]=merge(lc[x],lc[y]);
    		rc[o]=merge(rc[x],rc[y]);
    		return o;
    	}
    	int query(int x,int l,int r,int ql,int qr){
    		if(!x) return 0;
    		if(ql<=l&&r<=qr) return siz[x];
    		int mid=l+r>>1;
    		if(qr<=mid) return query(lc[x],l,mid,ql,qr);
    		if(ql>mid) return query(rc[x],mid+1,r,ql,qr);
    		return query(lc[x],l,mid,ql,qr)+query(rc[x],mid+1,r,ql,qr);
    	}
    }
    // Suffix Automaton
    vector<int> vec[N];
    namespace S1{ // S
    	vector<int> e[N];
    	int last=1,tot=1;
    	int ch[N][26],fa[N],len[N],rt[N];
    	void extend(int c,int po){
    		int p=last,cur=last=++tot;
    		T::insert(rt[cur],1,n,po);
    		for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
    		if(!p) fa[cur]=1;
    		else{
    			int q=ch[p][c];
    			if(len[q]==len[p]+1) fa[cur]=q;
    			else{
    				int clone=++tot;
    				memcpy(ch[clone],ch[q],sizeof ch[q]);
    				fa[clone]=fa[q],len[clone]=len[p]+1;
    				fa[cur]=fa[q]=clone;
    				for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
    			}
    		}
    	}
    	void dfs(int u){
    		for(int i=0;i<e[u].size();++i)
    			dfs(e[u][i]),rt[u]=T::merge(rt[u],rt[e[u][i]]);
    	}
    	void build_tree(){
    		for(int i=2;i<=tot;++i) e[fa[i]].push_back(i);
    		dfs(1);
    	}
    	void solve(char*s,int L,int R){
    		int length=strlen(s+1);
    		for(int i=1,p=1,now=0;i<=length;++i){
    			int c=s[i]-'a';
    			while(!ch[p][c]&&p) p=fa[p],now=len[p];
    			if(!p) {p=1,now=0;continue;}
    			p=ch[p][c],++now;
    			while(p>1){
    				if(T::query(rt[p],1,n,L+now-1,R)) break;
    				if(--now==len[fa[p]]) p=fa[p];
    			}
    			if(p==1) continue;
    			for(int j=0;j<vec[i].size();++j)
    				res[vec[i][j]]=max(res[vec[i][j]],now);
    		}
    	}
    }
    namespace S2{ // T
    	int last,tot;
    	int ch[N][26],fa[N],len[N];
    	void clear(){
    		for(int i=1;i<=tot;++i){
    			fa[i]=len[i]=res[i]=0;
    			memset(ch[i],0,sizeof ch[i]);
    		}
    		last=tot=1;
    	}
    	void extend(int c,int po){
    		int p=last,cur=last=++tot;
    		len[cur]=len[p]+1,vec[po].push_back(cur);
    		for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
    		if(!p) fa[cur]=1;
    		else{
    			int q=ch[p][c];
    			if(len[q]==len[p]+1) fa[cur]=q;
    			else{
    				int clone=++tot;
    				memcpy(ch[clone],ch[q],sizeof ch[q]);
    				fa[clone]=fa[q],len[clone]=len[p]+1,vec[po].push_back(clone);
    				fa[cur]=fa[q]=clone;
    				for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
    			}
    		}
    	}
    	void solve(){
    		ll ans1=0,ans2=0;
    		for(int i=1;i<=tot;++i){
    			if(res[i]>len[fa[i]]) ans2+=min(res[i],len[i])-len[fa[i]];
    			ans1+=len[i]-len[fa[i]];
    		}
    		printf("%lld
    ",ans1-ans2);
    	}
    }
    int main(){
    //	freopen("name.in","r",stdin),freopen("name.out","w",stdout);
    	scanf("%s",s+1),n=strlen(s+1);
    	for(int i=1;i<=n;++i) S1::extend(s[i]-'a',i);
    	S1::build_tree();
    	read(q);
    	for(int m,L,R;q--;){
    		scanf("%s",s+1),m=strlen(s+1);
    		read(L),read(R);
    		for(int i=1;i<=m;++i) vec[i].clear();
    		S2::clear();
    		for(int i=1;i<=m;++i) S2::extend(s[i]-'a',i);
    		S1::solve(s,L,R),S2::solve();
    	}
    	return 0;
    }
    
  • 相关阅读:
    前台组件
    IntelliJ IDEA 简体中文专题教程
    干货分享 | 创业公司绝对不会告诉你他们在用的工具们
    线程的基本概念 / 计算机程序的思维逻辑
    Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)
    【转载】 准人工智能分享Deep Mind报告 ——AI“元强化学习”
    (待续)【转载】 DeepMind发Nature子刊:通过元强化学习重新理解多巴胺
    【转载】 机器学习算法岗的常见面试问题及准备
    【转载】 180623 Conda install 本地压缩包文件tar.bz2
    2017年 某次 实验室会议 —— 记
  • 原文地址:https://www.cnblogs.com/autoint/p/10907556.html
Copyright © 2020-2023  润新知