6717. 【2020.06.07省選模擬】T3 敏感詞

題目


正解

辣雞數據結構題。
考慮某個串出現的所有位置的右端點。設相鄰位置之差爲did_id1=+d_1=+\infty),於是它覆蓋的位置爲min{di,len}\sum min\{d_i,len\},也就是dilendi+len[di>len]\sum_{d_i\leq len}d_i+len\sum [d_i>len]
於是考慮維護值在某個區間內的did_i的和、個數。(樹狀數組)

SAMSAM,建出failfail樹,對於每個節點,如果我們得到了它的rightright集合,二分lenlen,試着找到覆蓋位置恰好爲kk的字符串。
接下來就是維護rightright集合和樹狀數組。
直接上dsu on treedsu\ on \ tree,用setset維護rightright集合。

至於最終怎麼比較字符串的大小,可以直接字符串哈希二分;或者一開始建反串,這樣跑SAMSAM之後建出的failfail樹就是後綴樹(記得欽定failfail邊的順序,通過位置srfailmaxlens_{r-fail_{maxlen}}來比較大小,其中rrrightright集合中隨便一個點)。
時間複雜度O(nlg2n)O(n\lg^2 n)


代碼

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#define N 40010
#define ll long long
int n,k;
char str[N];
struct Node{
	Node *c[26],*fail;
	int len;
} d[N];
int cnt;
Node *S,*T;
Node *newnode(){
	Node *nw=&d[++cnt];
	memset(nw,0,sizeof(Node));
	return nw;
}
void insert(char ch){
	ch-='a';
	Node *nw=newnode();
	nw->len=T->len+1;
	Node *p=T;
	for (;p && !p->c[ch];p=p->fail)
		p->c[ch]=nw;
	if (!p)
		nw->fail=S;
	else{
		Node *q=p->c[ch];
		if (q->len==p->len+1)
			nw->fail=q;
		else{
			Node *clone=newnode();
			memcpy(clone,q,sizeof(Node));
			clone->len=p->len+1;
			for (;p && p->c[ch]==q;p=p->fail)
				p->c[ch]=clone;
			nw->fail=q->fail=clone;
		}
	}
	T=nw;
}
struct EDGE{
	int to;
	EDGE *las;
} e[N];
int ne;
EDGE *last[N];
int fa[N];
int pos[N];
ll t0[N],t1[N];
void add(int x,int c,ll t[]){
	for (;x<=n;x+=x&-x)
		t[x]+=c;
}
ll query(int x,ll t[]){
	ll r=0;
	for (;x;x-=x&-x)
		r+=t[x];
	return r;
}
int siz[N],hs[N];
int minr[N];
void init(int x){
	siz[x]=1,hs[x]=0;
	minr[x]=(pos[x]!=-1?pos[x]:n+1);
	for (EDGE *ei=last[x];ei;ei=ei->las){
		init(ei->to);
		siz[x]+=siz[ei->to];
		if (siz[ei->to]>siz[hs[x]])
			hs[x]=ei->to;
		minr[x]=min(minr[x],minr[ei->to]);
	}
}
set<int> s;
int f[N];
void addi(int i,int c){add(i,i*c,t1);add(i,c,t0);}
void geti(int i){
	if (s.empty()){
		addi(n,1);
		s.insert(i);
		return;
	}
	auto p=s.upper_bound(i);
	if (p==s.end()){
		--p;
		addi(i-*p,1);
	}
	else{
		if (p==s.begin()){
			addi(*p-i,1);
		}
		else{ 
			auto q=p;
			--q;
			addi(*p-*q,-1);
			addi(*p-i,1),addi(i-*q,1);
		}
	}
	s.insert(i);
}
void erasei(int i){
	s.erase(i);
	if (s.empty()){
		addi(n,-1);
		return;
	}
	auto p=s.upper_bound(i);
	if (p==s.end()){
		--p;
		addi(i-*p,-1);
	}
	else{
		if (p==s.begin()){
			addi(*p-i,-1);
		}
		else{
			auto q=p;
			--q;
			addi(*p-i,-1),addi(i-*q,-1);
			addi(*p-*q,1);
		}
	}
}
int calc(int len){return query(len,t1)+(query(n,t0)-query(len,t0))*len;}
void insert(int x){
	if (pos[x]!=-1)
		geti(pos[x]);
	for (EDGE *ei=last[x];ei;ei=ei->las)
		insert(ei->to);
}
void clear(int x){
	if (pos[x]!=-1)
		erasei(pos[x]);
	for (EDGE *ei=last[x];ei;ei=ei->las)
		clear(ei->to);
}
void dfs(int x){
	if (hs[x]){
		for (EDGE *ei=last[x];ei;ei=ei->las)
			if (ei->to!=hs[x]){
				dfs(ei->to);
				clear(ei->to);
			}
		dfs(hs[x]);
		for (EDGE *ei=last[x];ei;ei=ei->las)
			if (ei->to!=hs[x])
				insert(ei->to);
	}
	if (x==1)
		return;
	if (pos[x]!=-1)
		geti(pos[x]);
	int l=d[x].fail->len+1,r=d[x].len,res=-1;
	while (l<=r){
		int mid=l+r>>1,tmp=calc(mid);
		if (tmp==k){
			res=mid;
			break;
		}
		if (tmp>k)
			r=mid-1;
		else
			l=mid+1;
	}
	f[x]=res;
}
const int mo1=1300000003;
const int mo2=1000000009;
const int mo3=1000000007;
ll pro1[N],pro2[N],pro3[N];
ll pw1[N],pw2[N],pw3[N];
bool comp(int x,int n,int y,int m){
	if (n!=m)
		return n<m;
	int l=1,r=n,t=0;
	while (l<=r){
		int mid=l+r>>1;
		if (((pro1[x+mid]-pro1[x]*pw1[mid])%mo1+mo1)%mo1==
			((pro1[y+mid]-pro1[y]*pw1[mid])%mo1+mo1)%mo1 && 
			((pro2[x+mid]-pro2[x]*pw2[mid])%mo2+mo2)%mo2==
			((pro2[y+mid]-pro2[y]*pw2[mid])%mo2+mo2)%mo2 &&
			((pro3[x+mid]-pro3[x]*pw3[mid])%mo3+mo3)%mo3==
			((pro3[y+mid]-pro3[y]*pw3[mid])%mo3+mo3)%mo3)
			l=(t=mid)+1;
		else
			r=mid-1;
	}
	return str[x+t]<str[y+t];
}
int main(){
	freopen("that.in","r",stdin);
	freopen("that.out","w",stdout);
	int Q;
	scanf("%d",&Q);
	while (Q--){
		scanf("%s%d",str,&k);
		n=strlen(str);
		pw1[0]=pw2[0]=pw3[0]=1;
		for (int i=1;i<=n;++i){
			pw1[i]=pw1[i-1]*37%mo1;
			pw2[i]=pw2[i-1]*43%mo2;
			pw3[i]=pw3[i-1]*53%mo3;
		}
		pro1[0]=pro2[0]=pro3[0]=0;
		for (int i=1;i<=n;++i){
			pro1[i]=(pro1[i-1]*37+str[i-1]-'a')%mo1;
			pro2[i]=(pro2[i-1]*43+str[i-1]-'a')%mo2;	
			pro3[i]=(pro3[i-1]*53+str[i-1]-'a')%mo3;
		}
		cnt=0;
		memset(pos,255,sizeof pos);
		T=S=newnode();
		for (int i=0;i<n;++i){
			insert(str[i]);
			pos[T-d]=i;
		}
		ne=0;
		memset(last,0,sizeof last);
		for (int i=2;i<=cnt;++i){
			fa[i]=d[i].fail-d;
			e[ne]={i,last[fa[i]]};
			last[fa[i]]=e+ne++;
		}
		init(1);
		memset(t0,0,sizeof t0);
		memset(t1,0,sizeof t1);
		s.clear();
		dfs(1);
		int x=0,len=n+1;
		for (int i=2;i<=cnt;++i)
			if (f[i]!=-1){
				int _x=minr[i]-f[i]+1,_len=f[i];
				if (comp(_x,_len,x,len)){
					x=_x,len=_len;
				}
			}
		if (x==0 && len==n+1)
			printf("NOTFOUND!\n");
		else{
			str[x+len]=0;
			printf("%s\n",str+x);
		}
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章