The 15th Chinese Northeast L. k-th Smallest Common Substring

题意描述:
给你多个串,有多个询问,每次询问需要查询第k小所有模式串的共有子串,如果没有,输出-1,如果有输出该子串在第一个串中的第一次出现的位置

思路:
判断子串是否为公共串只需要染色即可,弦论那题可以询问第k小字典序子串,但是询问过多,明显会T,所以要转换思路,我们如果对字符串建反串,可以发现从反串的父节点出发,可以根据父结点与子节点相交位置来排序,然后根据这个方向去dfs的dfs序,就是字典序,如假设翻转串eabc,edbc的父结点都为bc,那根据dfs序明显先去eabc,而eabc的原串也确实是字典序小与edbc的原串的,所以我们根据建立的dfs序来遍历即是按照字典序来遍历了,利用前缀和二分思想即可查询第k小字典序,因为需要找在第一个串中的第一个出现位置,即翻转串的endpos最大的位置,所以在插入的时候再记录一个1串的endpos,再dfs一遍将所有在一串的子串endpos取大,最后输出答案即可,注意前缀和可能爆int,所以要将前缀和设为long long

#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int N=1e6+10;
typedef long long ll;
int n,m,q,tot,last,len[N],fa[N],ch[N][26];
ll pre[N];
int col[N],vis[N],endps[N];
pair<int,int>endpos[N];
string s;
string ss[N];
struct point {
	int to,w;
};
vector<point>v[N];
int cmp(point a,point b) {
	return a.w<b.w;
}

int Ins(int c,int last,pair<int,int>nowk) {
	int kk=nowk.first;
	int kpos=nowk.second;
	int p=last;
	if(ch[p][c]) {
		int q=ch[p][c];
		if(len[p]+1==len[q]) {
			return q;
		} else {
			int nq=++tot;
			len[nq]=len[p]+1;
			endpos[nq]=endpos[q];
			endps[nq]=endps[q];
			memcpy(ch[nq],ch[q],sizeof(ch[nq]));
			fa[nq]=fa[q];
			fa[q]=nq;
			for(; p&&ch[p][c]==q; p=fa[p])ch[p][c]=nq;
			return nq;
		}
	}
	int np=++tot;
	len[np]=len[p]+1;
	endpos[tot]=nowk;
	if(kk==1)endps[tot]=kpos;
	for(; p&&!ch[p][c]; p=fa[p])ch[p][c]=np;
	if(!p)fa[np]=1;
	else {
		int q=ch[p][c];
		if(len[p]+1==len[q])fa[np]=q;
		else {
			int nq=++tot;
			len[nq]=len[p]+1;
			endpos[nq]=endpos[q];
			endps[nq]=endps[q];
			memcpy(ch[nq],ch[q],sizeof(ch[nq]));
			fa[nq]=fa[q];
			fa[q]=fa[np]=nq;
			for(; p&&ch[p][c]==q; p=fa[p])ch[p][c]=nq;
		}
	}
	return np;
}
int pot[N];
int tid;
int viss[N];
inline void dfs(int x) {
	sort(v[x].begin(),v[x].end(),cmp);
	for(int i=0; i<v[x].size(); i++) {
		int y=v[x][i].to;
		if(vis[y]==m) {
			pot[++tid]=y;
			dfs(y);
		}
	}
}
inline void dfs1(int x) {
	for(int i=0; i<v[x].size(); i++) {
		int y=v[x][i].to;
		dfs1(y);
		endps[x]=max(endps[x],endps[y]);
	}
}
signed main() {
	ios::sync_with_stdio(false);
	int T;
	cin>>T;
	while(T--) {
		for(int i=0;i<=tot+10;i++){
			memset(ch[i],0,sizeof(ch[i]));
			col[i]=0;
			vis[i]=0;
			len[i]=0;
			fa[i]=0;
			pot[i]=0;
			endps[i]=0;
			v[i].clear();
			endpos[i].first=0;
			endpos[i].second=0;
			endps[i]=0;
		}
		cin>>m;
		tot=1;
		tid=0;
		for(int i=1; i<=m; i++) {
			last=1;
			cin>>ss[i];
			n=ss[i].length();
			reverse(ss[i].begin(),ss[i].end());
			for(int j=0; j<n; j++)
				last=Ins(ss[i][j]-'a',last, {i,j+1});
		}
		for(int i=1; i<=m; i++) {
			int pos=1;
			int len=ss[i].length();
			for(int j=0; j<len; j++) {
				pos=ch[pos][ss[i][j]-'a'];
				int p=pos;
				for(; p&&col[p]!=i; p=fa[p]) {
					if(i==1)viss[p]++;
					vis[p]++;
					col[p]=i;
				}
			}
		}
		for(int i=2; i<=tot; i++) {
			v[fa[i]].push_back({i,ss[endpos[i].first][endpos[i].second-len[fa[i]]-1]-'a'});
		}
		dfs(1);
		dfs1(1);
		for(int i=1; i<=tid; i++) {
			pre[i]=pre[i-1]+len[pot[i]]-len[fa[pot[i]]];
		}
		cin>>q;
		for(int i=1;i<=q;i++) {
			int x;
			cin>>x;
			if(x>pre[tid])cout<<-1<<'\n';
			else {
				int sid=lower_bound(pre+1,pre+1+tid,x)-pre;
				int l = ss[1].length()-endps[pot[sid]];
				int r = l+len[fa[pot[sid]]]+( x-pre[sid-1]);
				cout<<l<<" "<<r<<'\n';
			}
		}
	}
}

posted @ 2021-09-22 20:37  wlhp  阅读(66)  评论(0)    收藏  举报