zoukankan      html  css  js  c++  java
  • L. kth Smallest Common Substring(后缀自动机)

    GYM103145L. k-th Smallest Common Substring

    给出\(n\)个字符串。

    询问第\(k\)小的公共子串。

    做法:

    将每个字符串逆序插入SAM,建出后缀树。

    第一个需求,公共子串:

    这里可以把每个字符串视为一种颜色,它在SAM上对应的每个终止节点染上这个颜色。

    然后问题转化为询问一个节点的link树子树内是否有n种颜色。如果有,那么这个节点表示的子串就是公共子串。

    这一步可以用树上差分做掉。

    第二个需求,第k大。

    可以通过代码里给出的排序方法,对后缀树做一个字典序排序。

    这样dfs序就是字典序了。顺便完成了每个节点的endpos位置的存储。

    然后将所有公共子串节点按照dfs序排序。

    求一个前缀和数组,对每个询问k,在前缀和数组上二分,二分找到对应的节点,输出endpos位置,根据长度

    这题就做完了。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+10;
    int nxt[maxn][26],link[maxn],len[maxn],lst=1,tot=1;
    int c[maxn];
    pair<int,int> edp[maxn];
    int n,q;
    vector<int> e[maxn];
    void sam_extend (char c) {
    	int cur=++tot;
    	len[cur]=len[lst]+1;
    	int p=lst;
    	while (p&&!nxt[p][c-'a']) {
    		nxt[p][c-'a']=cur;
    		p=link[p];
    	}
    	if (!p) link[cur]=1;
    	else {
    		int q=nxt[p][c-'a'];
    		if (len[p]+1==len[q]) {
    			link[cur]=q;
    		}
    		else {
    			int clone=++tot;
    			len[clone]=len[p]+1;
    			for (int i=0;i<26;i++) nxt[clone][i]=nxt[q][i];
    			link[clone]=link[q];
    			while (p&&nxt[p][c-'a']==q) {
    				nxt[p][c-'a']=clone;
    				p=link[p];
    			}
    			link[q]=link[cur]=clone;
    		}
    	}
    	lst=cur;
    }
     
    vector<int> g[maxn];
    int h[maxn],father[25][maxn],dfn[maxn],tol;
     
     
     
    void init () {
    	for (int i=0;i<=tot;i++) {
    		g[i].clear();
    		e[i].clear();
    		h[i]=dfn[i]=link[i]=len[i]=c[i]=0;
    		edp[i]=make_pair(0,0);
    		for (int j=0;j<26;j++) {
    			nxt[i][j]=0;
    		}
    	}
    	lst=tot=1;
    	tol=0;
    }
     
    void dfs (int u) {
    	dfn[u]=++tol;
    	for (int v:g[u]) {
    		father[0][v]=u;
    		h[v]=h[u]+1;
    		dfs(v);
    		edp[u]=max(edp[u],edp[v]);
    	}
    }
    int lca (int x,int y) {
    	if (h[x]<h[y]) swap(x,y);
    	for (int i=20;i>=0;i--) if (h[x]-h[y]>>i) x=father[i][x];
    	if (x==y) return x;
    	for (int i=20;i>=0;i--) {
    		if (father[i][x]!=father[i][y]) {
    			x=father[i][x];
    			y=father[i][y];
    		}
    	}
    	return father[0][x];
    }
    vector<int> vv;
    void cal (int u) {
    	dfn[u]=++tol;
    	for (int v:g[u]) {
    		cal(v);
    		c[u]+=c[v];
    	}
    }
    int _;
    long long sum[maxn];
    string s[maxn];
    int main () {
    	ios::sync_with_stdio(false);
    	cin>>_;
    	int ff=0;
    	if (_>1) ff=1;
    	while (_--) {
    		cin>>n;
    		for (int i=n;i>=1;i--) {
    			lst=1;
    			cin>>s[i];
    			int mm=s[i].size();
    			for (int j=s[i].size()-1;j>=0;j--) {
    				sam_extend(s[i][j]);
    				e[i].push_back(lst);
    				edp[lst]=max(edp[lst],{i,mm-j});
    			}
    		}
    		for (int i=2;i<=tot;i++) g[link[i]].push_back(i);
    		dfs(1);
    		for (int i=1;i<=20;i++) for (int j=1;j<=tot;j++) father[i][j]=father[i-1][father[i-1][j]];
    		for (int i=1;i<=n;i++) {
    			sort(e[i].begin(),e[i].end(),[&](int x,int y) {
    				return dfn[x]<dfn[y];
    			});
    			for (int j=0;j<e[i].size();j++) c[e[i][j]]++;
    			for (int j=0;j<e[i].size()-1;j++) {
    				c[lca(e[i][j],e[i][j+1])]--;
    			}
    		}
    		for (int i=1;i<=tot;i++) sort(g[i].begin(),g[i].end(),[&](int x,int y) {
    			return s[edp[x].first][s[edp[x].first].size()-edp[x].second+len[i]]<s[edp[y].first][s[edp[y].first].size()-edp[y].second+len[i]];	
    		});
    		vv.clear();
    		tol=0;
    		cal(1);
    		for (int i=2;i<=tot;i++) if (c[i]==n) vv.push_back(i);
    		sort(vv.begin(),vv.end(),[&](int x,int y) {
    			return dfn[x]<dfn[y];
    		});
    		int m=vv.size();
    		for (int i=1;i<=m;i++) sum[i]=sum[i-1]+len[vv[i-1]]-len[link[vv[i-1]]];
    		cin>>q;
    		while (q--) {
    			int k;
    			cin>>k;
    			int L=1,R=m,pp=-1;
    			while (L<=R) {
    				int mid=(L+R)>>1;
    				if (sum[mid]>=k) {
    					pp=mid;
    					R=mid-1;
    				}
    				else {
    					L=mid+1;
    				}
    			}
    			if (pp==-1) {
    				cout<<"-1\n";
    				continue;
    			}
    			k-=sum[pp-1];
    			int x=vv[pp-1];
    			//cout<<x<<'\n';
    			cout<<s[edp[vv[pp-1]].first].size()-edp[vv[pp-1]].second<<" "<<s[edp[vv[pp-1]].first].size()-edp[vv[pp-1]].second+len[link[vv[pp-1]]]+k<<'\n';
    		}
    		init();
    	}
    }
    
  • 相关阅读:
    如何快速搞定websocket
    websocket断网消息补发
    div嵌套多个点击事件,点击后如何阻止多次事件触发冒泡
    仿照 MediatR实现了一个中介者模式Publish功能,使用同MediatR
    git提交指南(超级详细)
    删除github中的文件夹
    抽取进程集成模式注册报错,OGG-08221,OCI Error ORA-44004 invalid qualified SQL Name
    Oracle-参数学习_no_or_expansion
    OGG19版本源端新增字段,目标端复制进程不报错,使用MAPALLCOLUMNS进行测试
    Oracle存储过程如何定位慢SQL?
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15541393.html
Copyright © 2011-2022 走看看