TsinsenA1484. two strings(罗干)

传送门:http://www.tsinsen.com/A1484

思路:首先对于添加操作,后缀数组是很难实现的,因为这会改变很多个后缀。

所以换一个思路,把操作离线,倒着实现所有操作,这样插入就变成删除了。

删除操作就好做得多了。

先把B串接到A串后,用特殊字符隔开。

有询问时,还是向上向下二分出一个合法区间,查询区间内有多少个可以匹配的且存在的串。

因为要查区间和,用树状数组维护一下即可。

记录AB串的开始结束为止,删除时移动这些指针,然后把对应的被删除的后缀从树状数组中删除,“复活”的后缀加入树状数组(因为随着B串的缩短,有的后缀又可以匹配了)即可。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn=200010;
using namespace std;
int n,m,q[maxn],sta,eda,stb,edb,lena,t1[maxn],t2[maxn],rank[maxn],sa[maxn],sum[maxn],h[maxn],st[maxn][20],cnt,ans[maxn];
char a[maxn<<1],b[maxn<<1],s[maxn],ch[10];
struct bit{
	int t[maxn];
	void change(int x,int add){for (;x<=n;x+=x&(-x)) t[x]+=add;}
	int query(int x){int res=0;for (;x;x-=x&(-x)) res+=t[x];return res;}
	int query(int l,int r){return query(r)-query(l-1);};
}T;

void getsa(){
	int *x=t1,*y=t2,p=0,m=255;
	for (int i=1;i<=n;i++) sum[x[i]=s[i]]++;
	for (int i=1;i<=m;i++) sum[i]+=sum[i-1];
	for (int i=1;i<=n;i++) sa[sum[x[i]]--]=i;
	for (int j=1;p<n;j<<=1,m=p){
		p=0;
		for (int i=n-j+1;i<=n;i++) y[++p]=i;
		for (int i=1;i<=n;i++) if (sa[i]>j) y[++p]=sa[i]-j;
		memset(sum,0,sizeof(sum));
		for (int i=1;i<=n;i++) sum[x[y[i]]]++;
		for (int i=1;i<=m;i++) sum[i]+=sum[i-1];
		for (int i=n;i;i--) sa[sum[x[y[i]]]--]=y[i];
		swap(x,y),x[sa[1]]=p=1;
		for (int i=2;i<=n;i++){
			if (y[sa[i]]!=y[sa[i-1]]||y[sa[i]+j]!=y[sa[i-1]+j]) p++;
			x[sa[i]]=p;
		}
	}
	memcpy(rank,x,sizeof(rank));}

void geth(){
	for (int i=1,j=0;i<=n;i++){
		if (rank[i]==1) continue;
		while (s[i+j]==s[sa[rank[i]-1]+j]) j++;
		h[rank[i]]=j;
		if (j) j--;
	}
}

void prework(){
	for (int i=2;i<=n;++i) st[i][0]=h[i];
    for (int k=1;k<=19;++k)
        for (int j=1<<k;j<=n;++j)
            st[j][k]=min(st[j][k-1],st[j-(1<<(k-1))][k-1]);
}

int getmin(int l,int r){
	if (l>=r) return n-sa[r]+1;
	int k=log2(r-l);
	return min(st[r][k],st[l+(1<<k)][k]);
}

int findl(int x,int len){
	int l=1,r=x,ans;
	while (l<=r){
		int mid=(l+r)>>1;
		if (getmin(mid,x)>=len) ans=mid,r=mid-1;
		else l=mid+1;
	}
	return ans;
}

int findr(int x,int len){
	int l=x,r=n,ans;
	while (l<=r){
		int mid=(l+r)>>1;
		if (getmin(x,mid)>=len) ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans;
}

int query(){
	int pos=rank[lena+stb],len=edb-stb+1;
	return T.query(findl(pos,len),findr(pos,len));
}

void work(){
	for (int i=1;i<=eda-edb+1;i++) T.change(rank[i],1);
	for (int i=m;i;i--){
		if (q[i]==1) {if (eda-sta+1>=edb-stb+1) T.change(rank[sta],-1),++sta;}
		else if (q[i]==2) {if (eda-sta+1>=edb-stb+1) T.change(rank[eda-(edb-stb+1)+1],-1),--eda;}
		else if (q[i]==3) {++stb;if (eda-sta+1>=edb-stb+1) T.change(rank[eda-(edb-stb+1)+1],1);}
		else if (q[i]==4) {--edb;if (eda-sta+1>=edb-stb+1) T.change(rank[eda-(edb-stb+1)+1],1);}
		else ans[++cnt]=query();
	}
	for (int i=cnt;i;i--) printf("%d\n",ans[i]);
}

int main(){
    scanf("%s%s%d",a+maxn,b+maxn,&m);
    sta=stb=maxn;eda=maxn+strlen(a+maxn)-1;edb=maxn+strlen(b+maxn)-1;
	for (int i=1;i<=m;i++){
		scanf("%d",&q[i]);
		if (q[i]==1) scanf("%s",ch),a[--sta]=ch[0];
		else if (q[i]==2) scanf("%s",ch),a[++eda]=ch[0];
		else if (q[i]==3) scanf("%s",ch),b[--stb]=ch[0];
		else if (q[i]==4) scanf("%s",ch),b[++edb]=ch[0];
		else continue;
	}
	for (int i=sta;i<=eda;i++) a[i-sta+1]=a[i];eda=eda-sta+1;sta=1;a[eda+1]=0;
	for (int i=stb;i<=edb;i++) b[i-stb+1]=b[i];edb=edb-stb+1;stb=1;b[edb+1]=0;
	for (int i=1;i<=eda;i++) s[++n]=a[i];s[++n]='$',lena=n;
	for (int i=1;i<=edb;i++) s[++n]=b[i];s[n+1]=0;
	getsa(),geth(),prework(),work();
	return 0;
}


你可能感兴趣的:(后缀数组)