• loj 2720 [NOI2018] 你的名字


    loj 2720 [NOI2018] 你的名字

    https://loj.ac/problem/2720

    有一个串 (S) , (Q) 次询问,每次给出一个串 (T) ,和 (l,r) .

    (T) 中有多少非空本质不同子串没有在 (S[l cdots r]) 中出现过

    (|S| le 5 imes 10^5, sum |T| le 10^6,1 le l le r le |S|)

    Tutorial

    https://dangxingyu.com/2018/10/30/noi2018-%E4%BD%A0%E7%9A%84%E5%90%8D%E5%AD%97/

    https://www.cnblogs.com/cjyyb/p/10646874.html

    考虑对于 (T) 建立SAM,想要对每个endpos等价类(节点)统计贡献.发现我们想要求出的,就是以其中 (T) 中每个位置作为结尾,在 (S[l cdots r]) 中出现过的最长后缀的长度.

    考虑通过在 (S) 的SAM上遍历以求得 (Len_i) 表示 (T[1 cdots i]) 的最长的在 (S[l cdots r]) 中出现过的后缀的长度.

    (l=1,r=n) ,那么可以维护当前节点 (u) ,当前长度 (now) .设当前加入字符为 (c=T_i) ,那么若存在 (ch_{u,c}) 则前进,否则跳到 (link_u)

    对于一般的情况,我们不仅要判断 (ch_{u,c}) 是否存在,还需要判断其代表的endpos等价类中出否出现过 ([l+now,r]) 中的位置.所以此时不能直接跳 (link) ,应该每次 (now) 减1.

    endpos等价类可以用线段树合并维护.

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

    Code

    SAM中的link不是有序的.

    线段树合并时,若还需要使用被合并的节点,那么需要新建一个节点返回.

    #include <cassert>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <vector>
    #define debug(...) fprintf(stdout,__VA_ARGS__)
    #define lson tree[u].ls,l,mid
    #define rson tree[u].rs,mid+1,r
    using namespace std;
    template<class T> void read(T &x) {
    	x=0; int f=1,ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=getchar();}
    	x*=f;
    }
    typedef long long ll;
    const int sigma=26;
    const int maxn=5e5+50,maxm=1e6+50;
    const int maxnode=maxm<<1;
    int n; char S[maxn];
    int m; char T[maxm];
    int Q;
    int Len[maxm];
    int root[maxn<<1];
    namespace seg {
    	const int maxnode=maxn*100;
    	int all;
    	struct node {
    		int ls,rs; 
    	} tree[maxnode];
    	void insert(int &u,int l,int r,int qp) {
    		if(!u) u=++all;
    		if(l==r) {
    			return;
    		}
    		int mid=(l+r)>>1;
    		if(qp<=mid) insert(lson,qp);
    		else insert(rson,qp);
    	}
    	int merge(int a,int b) {
    		if(!a||!b) return a+b;
    		int c=++all;
    		tree[c].ls=merge(tree[a].ls,tree[b].ls);
    		tree[c].rs=merge(tree[a].rs,tree[b].rs);
    		return c;
    	}
    	bool query(int u,int l,int r,int ql,int qr) {
    		if(ql>qr) return 0;
    		if(u==0) return 0;
    		if(l==ql&&r==qr) {
    			return 1;
    		}
    		int mid=(l+r)>>1;
    		if(qr<=mid) return query(lson,ql,qr);
    		else if(ql>mid) return query(rson,ql,qr);
    		else {
    			bool L=query(lson,ql,mid);
    			bool R=query(rson,mid+1,qr);
    			return L||R; 
    		}
    	}
    }
    vector<int> adj[maxn<<1];
    struct string_suffix_machine {
    	int las,all,len[maxnode],link[maxnode],endpos[maxnode],ch[maxnode][sigma];
    	inline int newnode() {
    		int u=++all;
    		memset(ch[u],0,sizeof(ch[u]));
    		return u;
    	}
    	void init() {
    		all=las=0,link[0]=-1,endpos[0]=-1;
    		memset(ch[0],0,sizeof(ch[0]));
    	}
    	void extend(int c,int pos) {
    		int cur=newnode(),p=las;
    		len[cur]=len[p]+1;
    		endpos[cur]=pos;
    		while(p!=-1&&!ch[p][c]) {
    			ch[p][c]=cur;
    			p=link[p];
    		}
    		if(p==-1) link[cur]=0;
    		else {
    			int q=ch[p][c];
    			if(len[q]==len[p]+1) link[cur]=q;
    			else {
    				int nq=newnode();
    				len[nq]=len[p]+1;
    				link[nq]=link[q];
    				endpos[nq]=pos;
    				memcpy(ch[nq],ch[q],sizeof(ch[q]));
    				link[cur]=link[q]=nq;
    				while(p!=-1&&ch[p][c]==q) {
    					ch[p][c]=nq;
    					p=link[p];
    				}
    			}
    		}
    		las=cur;
    	}
    	void dfs(int u) {
    		if(u!=0) seg::insert(root[u],0,n-1,endpos[u]);
    		for(int i=0;i<adj[u].size();++i) {
    			int v=adj[u][i];
    			dfs(v);
    			root[u]=seg::merge(root[u],root[v]);
    		}
    	}
    	void build() {
    		for(int i=1;i<=all;++i) {
    			adj[link[i]].push_back(i);
    		}
    		dfs(0);
    	}
    	void travel(char *T,int m,int l,int r) {
    		--l,--r;
    		int u=0,now=0;
    		for(int i=0;i<m;++i) {
    			int c=T[i]-'a';
    			while(true) {
    				if(ch[u][c]&&seg::query(root[ch[u][c]],0,n-1,l+now,r)) {
    					u=ch[u][c],++now;
    					break;
    				}
    				else {
    					if(u==0) {
    						assert(now==0);
    						break;
    					}
    					if(--now<=len[link[u]]) u=link[u];
    				}
    			}
    			Len[i]=now;
    		}
    	}
    	ll sol() {
    		ll an=0;
    		for(int i=1;i<=all;++i) {
    			an+=max(0,len[i]-max(Len[endpos[i]],len[link[i]]));
    		}
    		return an;
    	}
    } sam0,sam1;
    int main() {
    	freopen("name.in","r",stdin);
    	freopen("name.out","w",stdout);
    	scanf("%s",S),n=strlen(S);
    	sam0.init();
    	for(int i=0;i<n;++i) sam0.extend(S[i]-'a',i);
    	sam0.build();
    	read(Q);
    	while(Q--) {
    		scanf("%s",T),m=strlen(T);
    		int l,r; read(l),read(r);
    		sam1.init();
    		for(int i=0;i<m;++i) sam1.extend(T[i]-'a',i);
    		sam0.travel(T,m,l,r);
    		printf("%lld
    ",sam1.sol());
    	}
    	return 0;
    }
    
  • 相关阅读:
    Linux系统的tomcat以daemon模式启动并配置成服务202004
    HBuilder android 开发者证书的生成20200416
    【office相关问题】Excel无法打开文件xxx.xlsx,因为文件格式或文件扩展名无效。请确定文件未损坏解决办法
    RHEL6.4服务器整体迁移记录RHEL6.9-2020-操作系统安装在M2固态,应用/数据库重要数据安装至DELL-H310阵列卡RAID1上保证数据安全性
    Linux下将自己安装的Apache(httpd)新增为系统服务,开机自启动
    Linux安装mysql(解压版)tar包解压安装(靠谱版)
    koa2实现jwt登录
    详解js数组扁平化
    快速掌握ES6 iterator Generator和async 之间的关系及其用法
    快速掌握ES6的class用法
  • 原文地址:https://www.cnblogs.com/ljzalc1022/p/13446886.html
Copyright © 2020-2023  润新知