CodeForces 547E Mike and Friends [后缀树组+主席树]

本文介绍了一种利用AC自动机建立失败树和后缀数组来解决子串查询问题的高效算法。通过AC自动机实现字符串匹配,结合后缀数组快速查找区间内所有子串的出现次数,从而实现复杂度优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

What-The-Fatherland is a strange country! All phone numbers there are strings consisting of lowercase English letters. What is double strange that a phone number can be associated with several bears!

In that country there is a rock band called CF consisting of n bears (including Mike) numbered from 1 to n.

Phone number of i-th member of CF is si. May 17th is a holiday named Phone Calls day. In the last Phone Calls day, everyone called all the numbers that are substrings of his/her number (one may call some number several times). In particular, everyone called himself (that was really strange country).

Denote as call(i, j) the number of times that i-th member of CF called the j-th member of CF.

The geek Mike has q questions that he wants to ask you. In each question he gives you numbers l, r and k and you should tell him the number


题意:给出N个串,M个询问,对于每个询问L,R,K,给出串L到串R的所有串中串K作为子串出现了几次。

解法:正解是AC自动机建Fail树然后根据DFS序乱搞,但是脑洞太小,只能想出比较朴素的后缀数组+主席树的解法。

可知,将所有串并成一个串做完SA后,是可以二分求出每个串作为子串的区间(Lx , Rx)的 (SA中的下标进行二分),二分过程只要比较一下LCP与原串的长度即可。然后只要询问这个区间中所有的后缀所属于的串的编号,在询问的区间(L,R)中的个数。这个过程可以用主席树实现。比较卡常数,不过稍微改下就能过。

代码: (这里后缀数组使用了DC3实现)

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<stdlib.h>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<bitset>
#pragma comment(linker, "/STACK:1024000000,1024000000")
template <class T>
bool scanff(T &ret){ //Faster Input
    char c; int sgn; T bit=0.1;
    if(c=getchar(),c==EOF) return 0;
    while(c!='-'&&c!='.'&&(c<'0'||c>'9')) c=getchar();
    sgn=(c=='-')?-1:1;
    ret=(c=='-')?0:(c-'0');
    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    if(c==' '||c=='\n'){ ret*=sgn; return 1; }
    while(c=getchar(),c>='0'&&c<='9') ret+=(c-'0')*bit,bit/=10;
    ret*=sgn;
    return 1;
}
#define inf 1073741823
#define llinf 4611686018427387903LL
#define PI acos(-1.0)
#define lth (th<<1)
#define rth (th<<1|1)
#define rep(i,a,b) for(int i=int(a);i<=int(b);i++)
#define drep(i,a,b) for(int i=int(a);i>=int(b);i--)
#define gson(i,root) for(int i=ptx[root];~i;i=ed[i].next)
#define tdata int testnum;scanff(testnum);for(int cas=1;cas<=testnum;cas++)
#define mem(x,val) memset(x,val,sizeof(x))
#define mkp(a,b) make_pair(a,b)
#define findx(x) lower_bound(b+1,b+1+bn,x)-b
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define lowbit(x) (x&(-x))

#define NN 800040
#define SIZZ 256
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)

int aa[3*NN],bb[NN],cc[NN],xx[NN],yy[NN];
int sa[3*NN],height[NN],rank[NN];
int hf[NN][25];
char s[NN];

int c0(int *r,int a,int b){
	return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
}
int c12(int k,int *r,int a,int b){
	if(k==2)return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
	else return r[a]<r[b]||r[a]==r[b]&&cc[a+1]<cc[b+1];
}

void sort(int *r,int *a,int *b,int n,int m){
	int i;
	for(i=0;i<n;i++)cc[i]=r[a[i]];
	for(i=0;i<m;i++)bb[i]=0;
	for(i=0;i<n;i++)bb[cc[i]]++;
	for(i=1;i<m;i++)bb[i]+=bb[i-1];
	for(i=n-1;i>=0;i--)b[--bb[cc[i]]]=a[i];
}
void dc3(int *r,int *sa,int n,int m){
	int i,j,ta=0,tb=(n+1)/3,tc=0,p;
	int *rn=r+n,*sn=sa+n;
	r[n]=r[n+1]=0;
	for(i=0;i<n;i++)if(i%3!=0)xx[tc++]=i;
	sort(r+2,xx,yy,tc,m);
	sort(r+1,yy,xx,tc,m);
	sort(r,xx,yy,tc,m);
	for(p=1,rn[F(yy[0])]=0,i=1;i<tc;i++)
	rn[F(yy[i])]=c0(r,yy[i-1],yy[i])?p-1:p++;
	if(p<tc)dc3(rn,sn,tc,p);
	else for(i=0;i<tc;i++)sn[rn[i]]=i;
	for(i=0;i<tc;i++)if(sn[i]<tb)yy[ta++]=sn[i]*3;
	if(n%3==1)yy[ta++]=n-1;
	sort(r,yy,xx,ta,m);
	for(i=0;i<tc;i++)cc[yy[i]=G(sn[i])]=i;
	for(i=0,j=0,p=0;i<ta&&j<tc;p++)
	sa[p]=c12(yy[j]%3,r,xx[i],yy[j])?xx[i++]:yy[j++];
	for(;i<ta;p++)sa[p]=xx[i++];
	for(;j<tc;p++)sa[p]=yy[j++];
}
void calh(int n){
	int i,j,k=0;
	for(i=0;i<n;i++)rank[sa[i]]=i;
	for(i=0;i<n;i++){
		if(k)k--;
		j=sa[rank[i]-1];
		if(rank[i]-1<0)continue;
		while(i+k<n-1&&j+k<n-1&&aa[i+k]==aa[j+k])k++;
		height[rank[i]]=k;
	}
	for(i=0;i<n;i++)hf[i][0]=height[i];
	for(j=1;j<=log(n*1.0)/log(2.0);j++){
		for(i=0;i<n;i++){
			if(i+(1<<j)<n){
				hf[i][j]=min(hf[i][j-1],hf[i+(1<<(j-1))][j-1]);
			}
		}
	}
}

int lcp(int x,int y){
	int i=rank[x];
	int j=rank[y];
	if(j<i)swap(i,j);
	int k=log(j-i)*1.0/log(2.0);
	return min(hf[i+1][k],hf[j-(1<<k)+1][k]);
}


int n,m,qn,ql,qr;
int len[NN];
int idx[NN];
int belong[NN];


#define MM 10000000
int tot=0;
int t[NN];
int c[MM],lson[MM],rson[MM];

int build(int l,int r){
    int x=++tot;
    if(l!=r){
        int m=(l+r)>>1;
        lson[x]=build(l,m);
        rson[x]=build(m+1,r);
    }
    return x;
}


void update(int l,int r,int pos,int pre,int cur,int val){
    lson[cur]=lson[pre];
    rson[cur]=rson[pre];
    c[cur]=c[pre]+val;
    if(l==r)return;
    int m=(l+r)>>1;
    if(pos<=m)update(l,m,pos,lson[pre],lson[cur]=++tot,val);
    else update(m+1,r,pos,rson[pre],rson[cur]=++tot,val);
}

int gettop(int idx,int len){
    int l=0,r=idx;
    while(l+1<r){
        int m=(l+r)>>1;
        if(lcp(sa[m],sa[idx])>=len)r=m;
        else l=m;
    }
    if(lcp(sa[l],sa[idx])>=len)return l;
    return r;
}

int getbutton(int idx,int len){
    int l=idx,r=n-1;
    while(l+1<r){
        int m=(l+r)>>1;
        if(lcp(sa[m],sa[idx])>=len)l=m;
        else r=m;
    }
    if(lcp(sa[r],sa[idx])>=len)return r;
    return l;
}

int query(int l,int r,int x){
    if(l>=ql&&r<=qr)return c[x];
    int sum=0;
    int m=(l+r)>>1;
    if(m>=ql)sum+=query(l,m,lson[x]);
    if(m<qr)sum+=query(m+1,r,rson[x]);
    return sum;
}

int lx[NN];
int rx[NN];
main(){
    n=0;
    scanff(m);
    scanff(qn);
	rep(i,1,m){
		scanf("%s",s);
		len[i]=strlen(s);
		idx[i]=n;
		rep(j,0,len[i]-1){
		    aa[n++]=s[j];
		    belong[n-1]=i;
		}
		aa[n++]=SIZZ+i;
		belong[n-1]=i;
	}
	dc3(aa,sa,n,SIZZ+m+10);
	calh(n);
	build(1,m);
	rep(i,0,n)t[i]=1;
    rep(i,1,n){
        if(aa[sa[i-1]]>=SIZZ){
            n=i;
            break;
        }
        t[i]=++tot;
        update(1,m,belong[sa[i-1]],t[i-1],t[i],1);
    }
    rep(i,1,qn){
        int k;
        scanff(ql);
        scanff(qr);
        scanff(k);
        int l,r;
        if(lx[k])l=lx[k];
        else l=gettop(rank[idx[k]],len[k])+1,lx[k]=l;
        if(rx[k])r=rx[k];
        else r=getbutton(rank[idx[k]],len[k])+1,rx[k]=r;
        printf("%d\n",query(1,m,t[r])-query(1,m,t[l-1]));
    }
}





### Codeforces 887E Problem Solution and Discussion The problem **887E - The Great Game** on Codeforces involves a strategic game between two players who take turns to perform operations under specific rules. To tackle this challenge effectively, understanding both dynamic programming (DP) techniques and bitwise manipulation is crucial. #### Dynamic Programming Approach One effective method to approach this problem utilizes DP with memoization. By defining `dp[i][j]` as the optimal result when starting from state `(i,j)` where `i` represents current position and `j` indicates some status flag related to previous moves: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = ...; // Define based on constraints int dp[MAXN][2]; // Function to calculate minimum steps using top-down DP int minSteps(int pos, bool prevMoveType) { if (pos >= N) return 0; if (dp[pos][prevMoveType] != -1) return dp[pos][prevMoveType]; int res = INT_MAX; // Try all possible next positions and update 'res' for (...) { /* Logic here */ } dp[pos][prevMoveType] = res; return res; } ``` This code snippet outlines how one might structure a solution involving recursive calls combined with caching results through an array named `dp`. #### Bitwise Operations Insight Another critical aspect lies within efficiently handling large integers via bitwise operators instead of arithmetic ones whenever applicable. This optimization can significantly reduce computation time especially given tight limits often found in competitive coding challenges like those hosted by platforms such as Codeforces[^1]. For detailed discussions about similar problems or more insights into solving strategies specifically tailored towards contest preparation, visiting forums dedicated to algorithmic contests would be beneficial. Websites associated directly with Codeforces offer rich resources including editorials written after each round which provide comprehensive explanations alongside alternative approaches taken by successful contestants during live events. --related questions-- 1. What are common pitfalls encountered while implementing dynamic programming solutions? 2. How does bit manipulation improve performance in algorithms dealing with integer values? 3. Can you recommend any online communities focused on discussing competitive programming tactics? 4. Are there particular patterns that frequently appear across different levels of difficulty within Codeforces contests?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值