6717. 【2020.06.07省选模拟】T3 敏感词

题目


正解

辣鸡数据结构题。
考虑某个串出现的所有位置的右端点。设相邻位置之差为 d i d_i di d 1 = + ∞ d_1=+\infty d1=+),于是它覆盖的位置为 ∑ m i n { d i , l e n } \sum min\{d_i,len\} min{di,len},也就是 ∑ d i ≤ l e n d i + l e n ∑ [ d i > l e n ] \sum_{d_i\leq len}d_i+len\sum [d_i>len] dilendi+len[di>len]
于是考虑维护值在某个区间内的 d i d_i di的和、个数。(树状数组)

S A M SAM SAM,建出 f a i l fail fail树,对于每个节点,如果我们得到了它的 r i g h t right right集合,二分 l e n len len,试着找到覆盖位置恰好为 k k k的字符串。
接下来就是维护 r i g h t right right集合和树状数组。
直接上 d s u   o n   t r e e dsu\ on \ tree dsu on tree,用 s e t set set维护 r i g h t right right集合。

至于最终怎么比较字符串的大小,可以直接字符串哈希二分;或者一开始建反串,这样跑 S A M SAM SAM之后建出的 f a i l fail fail树就是后缀树(记得钦定 f a i l fail fail边的顺序,通过位置 s r − f a i l m a x l e n s_{r-fail_{maxlen}} srfailmaxlen来比较大小,其中 r r r r i g h t right right集合中随便一个点)。
时间复杂度 O ( n lg ⁡ 2 n ) O(n\lg^2 n) O(nlg2n)


代码

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值