hdu 4622(后缀自动机|后缀数组)

//昨天晚上一直用后缀数组来写,怎么写都超时(后来在网上看到也有用后缀数组过的,然来是自己昨晚sb,用把l,r区间的rank数组排序来使l,r区间内的rank有序,其实直接按rank,O(n)遍历就可以做到),

//今天看了多校3的官方解题,可以用后缀自动机;

//今天上午就去学习了后缀自动机(以前没有主动去学后缀自动机,总想后缀自动机能解的,后缀数组都能解,这次顺便学习一下);

//官方解题说这题后缀自动机可以O(n^2)+O(1),可是我写的后缀自动机时间复杂度还是O(n*q),因为每次计算字串的个数的还是把所有的step遍历了的一遍,

//但是没有重新建树,建树的常数小了一些;

//思考:(不知道计算字串的个数的时候可不可以利用以前的信息,也就是F数组的修改更新可不可以记录下来,去想想,继续学习后缀自动机);

//ac 代码如下:


#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include<math.h>
#include<bitset>
#include<string>
#include<queue>
#include<deque>
#include<ctype.h>
#include<vector>
#include<set>
#include<map>
#include<list>
#include<stack>
#include<functional>
#include<algorithm>

using namespace std;

#define N 2010

int F[N*2],ant,last,ch[N*2][26],step[N*2];
void init(){
    last=ant=1;
    memset(F,0,sizeof(F));
    memset(ch,0,sizeof(ch));
    memset(step,0,sizeof(step));
}
void ins(int x){
    int t=++ant,pa=last;
    step[t]=step[last]+1;last=t;
    for(;pa&&!ch[pa][x];pa=F[pa]) ch[pa][x]=t;
    if(pa==0) F[t]=1;
    else if(step[pa]+1==step[ch[pa][x]]) F[t]=ch[pa][x];
    else {
        int nq=++ant,q=ch[pa][x];
        memcpy(ch[nq],ch[q],sizeof(ch[nq]));
        step[nq]=step[pa]+1;F[nq]=F[q];F[q]=F[t]=nq;
        for(;pa&&ch[pa][x]==q;pa=F[pa]) ch[pa][x]=nq;
    }
}
//以上为后缀自动机的部分

char s[N];
int ans[10100];

vector<pair<int,int> > Q[N];

void run(int p){
    int i,t,j,tt;
    init();
    sort(Q[p].begin(),Q[p].end());
    for(i=0,t=p-1;i<(int)Q[p].size();i++){
        if(i&&Q[p][i].first==t){
            ans[Q[p][i].second]=tt; continue;
        }
        while(t<Q[p][i].first){
            ins(s[t]-'a');t++;
        }
        for(tt=0,j=ant;j>0;j--) tt+=step[j]-step[F[j]];
        ans[Q[p][i].second]=tt;
    }
}

int main(){
    int cas,n,i,a,b,m;
    scanf("%d",&cas);
    while(cas--){
        scanf("%s",s);m=strlen(s);
        for(i=1;i<=m;i++) Q[i].clear();
        scanf("%d",&n);
        for(i=1;i<=n;i++){
            scanf("%d%d",&a,&b);
            Q[a].push_back(make_pair(b,i));
        }
        for(i=1;i<=m;i++) if(Q[i].size()) run(i);
        for(i=1;i<=n;i++) printf("%d\n",ans[i]);
    }
	return 0;
}


//后缀数组修改后ac的代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>

using namespace std;

const int maxn = 2100;
char s[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
void build_sa(int n,int m){
	int i,k,*x=t,*y=t2,p;
	//基数排序
	for(i=0;i<m;i++)  c[i]=0;
	for(i=0;i<n;i++)  c[x[i]=s[i]]++;
	for(i=1;i<m;i++)  c[i]+=c[i-1];
	for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
	for(k=1;k<n;k*=2){
		p=0;
		//直接利用sa数组排序第二关键字
		for(i=n-k;i<n;i++)  y[p++]=i;
		for(i=0;i<n;i++)  if(sa[i]>=k) y[p++]=sa[i]-k;
		//基数排序第一关键字
		for(i=0;i<m;i++)  c[i]=0;
		for(i=0;i<n;i++)  c[x[y[i]]]++;
		for(i=1;i<m;i++)  c[i]+=c[i-1];
		for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
		//根据sa和y数组计算新的x数组
		swap(x,y);
		p=1; x[sa[0]]=0;
		for(i=1;i<n;i++)
			x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?p-1:p++;
		if(p>=n)break;
		m=p;
	}
}
int rank[maxn],height[maxn];
void getHeight(int n)
{
	int i,j,k=0;
	for(i=1;i<=n;i++) rank[sa[i]] = i;
	for(i=0;i<n;i++){
		if(rank[i]==1)continue;
		if(k)k--;
		j=sa[rank[i]-1];
		while(s[i+k]==s[j+k])k++;
		height[rank[i]]=k;
	}
}
int dp[maxn][maxn];
void brute_rmp(int n){
    int i,k,ans;
    for(k=1;k<=n;k++){
        ans=n+n;
        for(i=k+1;i<=n;i++){
            ans=min(ans,height[i]);
            dp[k][i]=ans;
        }
    }
}
struct node{
    int k,d;
}A[maxn];

int M;
int run(int l,int r)
{
    int n=r-l+1,i,ans=0,k;
    for(i=k=1;i<=M;i++){ //昨天就是sb在这儿(对A[i].k排序),不知道写这个循环来得到一个rank有序的A[]
        if(l<=sa[i]&&sa[i]<=r){
            A[k].k=i;A[k].d=r-sa[i]+1;k++;
        }if(k>n) break;
    }
    ans+=A[1].d;
    int b=1;
    for(i=2;i<=n;i++){
        ans+=A[i].d-min(A[i].d,min(A[b].d,dp[A[b].k][A[i].k]));
        if(A[b].d>A[i].d&&dp[A[b].k][A[i].k]>=A[i].d); else b=i;
    }
    return ans;
}
int main(){
    int cas,n,a,b;
    scanf("%d",&cas);
    while(cas--){
        scanf("%s",s);
        M=strlen(s);
        build_sa(M+1,255);
        getHeight(M);brute_rmp(M);
        scanf("%d",&n);
        while(n--){
            scanf("%d%d",&a,&b);
            printf("%d\n",run(a-1,b-1));
        }
    }
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值