【SAM复习】JZOJ6717. 【2020.06.11省选模拟】敏感词

Description

在这里插入图片描述

  • T<=10,|S|<=20000

Solution

  • 省选之前打SAM练练手(虽然跟重新学没什么区别),太久没有复习了,很多关于SAM的定义理解什么的都忘光了。
  • 这题就是建一个SAM,然后合并parent树上的endpos集合,可以直接dsu on tree.
  • 考虑对于一个节点,它可以选择的长度为L到R,二分一个长度,那么答案就是 ∑ m i n ( Δ i , l e n ) \sum min(\Delta i,len) min(Δi,len) Δ i \Delta i Δi是相邻的endpos的差。
  • 那么分类,对于 < = l e n <=len <=len的维护 ∑ Δ i \sum \Delta i Δi,对于 > l e n >len >len的维护它们的个数。可以在用set启发式合并的同时用树状数组维护。
  • 判断字典序可以哈希找LCP(十分清真),或者从后往前建后缀树,然后给每一条fail边记录一个相邻的字符,然后按照字符的大小从小往大遍历(但是并不好看)。
  • 然后就是两个log的了。

关于SAM

  • 每一个点对应一个 e n d p o s endpos endpos集合,它的转移边 t o [ c ] to[c] to[c]表示在它的所有 e n d p o s endpos endpos后加上 c c c之后对应的 e n d p o s endpos endpos集合。所以它们的 e n d p o s endpos endpos是包含关系。
  • 新加入一个点 x x x的时候,遍历上一个点的所有后缀,也就是不断跳fail的过程,如果当前跳的点没有c这条边,那么就直接连上,相当于是把endpos分裂了。
  • 如果当前点已经有c这条边,那么如果len[to]=len[x]+1,那么当前的后缀加上c也别的endpos也在to[c]里面,所以只需要把fail指过去,继承新的节点的endpos就好了。
  • 如果len[to]>len[x]+1,那么需要把to分裂成len[x]+1的部分和大于它的部分,因为由于新加进来的点的存在导致x的c边的endpos必须包括新点,但是多出来的部分的endpos与这个矛盾,所以需要分裂。
  • 然后把fail链上指向原来未分裂的点的点的c边指向分裂点。
  • 话说只需要不打错SAM,应该就没有什么好调试的了,还是比较好打的(主要是难以理解)。

关于后缀树

  • 在建立SAM的时候同时对于所有新点储存它的endpos,分裂点储存任意一个endpos(也可以不储存,这里的endpos主要是为了确定fail链的字符)。
  • 然后在连接fail的时候可以根据end顺便找到原串的某个字符,表示这条fail链的字符大小。
  • 后缀树每一个点的endpos即为子树内endpos的并。
  • 倒着建后缀树,两个节点的LCA即为它们的最长公共前缀。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#define maxn 40005
using namespace std;

int n,m,i,j,k,a[maxn],s[maxn];
struct node{
    
    int fa,to[26],end,len,p[26];} t[maxn];
int las,tot,rt;

struct Treearray{
    
    
	int s[maxn];
	void add(int x,int d){
    
    for(;x<=n+1;x+=x&-x) s[x]+=d;}
	int sum(int x,int S=0){
    
    for(;x>0;x-=x&-x) S+=s[x];return S;}
	void clr(){
    
    memset(s,0,sizeof(s));}
} T1,T2;

int newnode(){
    
    
	tot++; t[tot].fa=t[tot].end=t[tot].len=0;
	memset(t[tot].to,0,sizeof(t[tot].to));
	memset(t[tot].p,0,sizeof(t[tot].p));
	return tot;
}

void link(int x,int y){
    
    
	t[x].fa=y;
	if (t[y].p[s[t[x].end-t[y].len]])
		printf("!!!");
	t[y].p[s[t[x].end-t[y].len]]=x;
}

void del(int x,int y){
    
    
	t[y].p[s[t[x].end-t[y].len]]=0;
}

void add(int c,int End){
    
    
	int np=newnode(),p=las; las=np;
	t[np].end=End,t[np].len=t[p].len+1;
	for(;p&&!t[p].to[c];p=t[p].fa) t[p].to[c]=np;
	if (!p) link(np,rt); else {
    
    
		int q=t[p].to[c];
		if (t[q].len==t[p].len+1) link(np,q); else {
    
    
			int nq=newnode();
			t[nq]=t[q],t[nq].len=t[p].len+1;
			memset(t[nq].p,0,sizeof(t[nq].p));
			del(q,t[q].fa);
			link(nq,t[q].fa);
			link(np,nq),link(q,nq);
			for(;p&&t[p].to[c]==q;p=t[p].fa)
				t[p].to[c]=nq;
		}
	}
}

int S[maxn],tp,sz[maxn],g[maxn],totd,dfn[maxn];
void dfs(int x){
    
    
	sz[x]=1,g[x]=0,dfn[x]=++totd;
	for(int i=0;i<26;i++) if (t[x].p[i]) {
    
    
		dfs(t[x].p[i]),sz[x]+=sz[t[x].p[i]];
		if (!g[x]||sz[t[x].p[i]]>sz[g[x]])	
			g[x]=t[x].p[i];
	}
}

int ansl,ansr,ansi;
set<int> id;
set<int>::iterator it,it0,it1;
void insert(int i){
    
    
	it=id.lower_bound(i);
	if (it!=id.end()&&*it==i) return;
	if (it!=id.begin()&&it!=id.end()){
    
    
		it0=it,it0--;
		T1.add(*it-*it0,-(*it-*it0));
		T2.add(n+1-(*it-*it0),-1);
	} 
	if (it!=id.end()){
    
    
		int j=*it;
		T1.add(j-i,j-i);
		T2.add(n+1-(j-i),1);
	}
	if (it!=id.begin()){
    
    
		it0=it,it0--;
		int j=*it0;
		T1.add(i-j,i-j);
		T2.add(n+1-(i-j),1);
	}
	id.insert(i);
}

void Del(int i){
    
    
	it=id.lower_bound(i);
	if (it==id.end()||*it!=i) return;
	it0=it,it0++; 
	if (it0!=id.end()){
    
    
		int j=*it0;
		T1.add(j-i,-(j-i));
		T2.add(n+1-(j-i),-1);
		if (it!=id.begin()){
    
    
			it1=it,it1--;
			int k=*it1;
			T1.add(j-k,j-k);
			T2.add(n+1-(j-k),1);
		}
	} 
	if (it!=id.begin()){
    
    
		it0=it,it0--; int j=*it0;
		T1.add(i-j,-(i-j));
		T2.add(n+1-(i-j),-1);
	}
	id.erase(it);
}

void clear(int x){
    
    
	for(int i=0;i<26;i++) if (t[x].p[i])
		clear(t[x].p[i]);
	Del(t[x].end);
}

void putin(int x){
    
    
	for(int i=0;i<26;i++) if (t[x].p[i])
		putin(t[x].p[i]);
	insert(t[x].end);
}

void dfs2(int x){
    
    
	for(int i=0;i<26;i++) if (t[x].p[i]!=g[x]&&t[x].p[i])	
		dfs2(t[x].p[i]),clear(t[x].p[i]);
	if (g[x]) dfs2(g[x]);
	for(int i=0;i<26;i++) if (t[x].p[i]!=g[x])
		putin(t[x].p[i]);
	insert(t[x].end);
	int l=t[t[x].fa].len+1,r=t[x].len,mid,p=0;
	while (l<=r){
    
    
		mid=(l+r)>>1;
		int tmp=T1.sum(mid)+T2.sum(n+1-mid-1)*mid;
		if (tmp<m) l=mid+1; else 
		if (tmp>m) r=mid-1; else 
			{
    
    p=mid;break;}
	}
	if (p){
    
    	
		if (!ansi||p<ansr-ansl+1
			||p==ansr-ansl+1&&(dfn[x]<=ansi&&ansi<=dfn[x]+sz[x]-1||ansi>dfn[x]))
		ansi=dfn[x],ansr=*(++id.begin()),ansl=ansr-p+1;
	}
}

int main(){
    
    
//	freopen("that.in","r",stdin);
//	freopen("that.out","w",stdout);
	int T;
	scanf("%d",&T);
	while (T--){
    
    
		char ch=getchar(); while (ch<'a'||ch>'z') ch=getchar();
		n=0; while (ch>='a'&&ch<='z') a[++n]=ch-'a',ch=getchar();
		tot=0; las=rt=newnode();
		T1.clr(),T2.clr();
		for(i=n;i>=1;i--) 
			s[n-i+1]=a[i],add(a[i],n-i+1);
		scanf("%d",&m);
		id.clear(),id.insert(0);
		totd=0,dfs(1);
		ansl=ansr=ansi=0,dfs2(1);
		if (ansi==0) printf("NOTFOUND!\n"); else {
    
    
			for(i=ansr;i>=ansl;i--) 
				putchar('a'+s[i]);
			putchar('\n');
		}
	}
}


猜你喜欢

转载自blog.csdn.net/qq_43649416/article/details/106724423