[后缀数组+枚举] hdu 4622 Reincarnation

来源:互联网 发布:自然语言处理 源码 编辑:程序博客网 时间:2024/05/24 01:55

题意:求一个字符串中,[a,b]内的不同子串数是多少。

思路:跟之前的求整个字符串中的不同子串数的方法一样,总的个数减去重复个数。

当然这题显然不能截取一段做一次DA,这样是超时的。

当然也不能选完在排序,我们其实可以种一个方法规避排序。

首先我们知道sa是排好序的了,那么我们只要标记一下[a,b]之间的ra[i],也就是sa里的位置。

这样我们遍历一遍sa,计算一下那些被标记的位置的lcp就好了!

代码:

#include"cstdlib"#include"cstdio"#include"cstring"#include"cmath"#include"queue"#include"algorithm"#include"iostream"#include"map"using namespace std;#define N 20020int wa[N],wb[N],wv[N],wws[N];int sa[N],ra[N],height[N],Log[N];int v[N];int cmp(int *r,int a,int b,int l){    return r[a]==r[b]&&r[a+l]==r[b+l];}void da(int n,int m){    int i,j,p,*x=wa,*y=wb;    for(i=0; i<m; i++) wws[i]=0;    for(i=0; i<n; i++) wws[x[i]=v[i]]++;    for(i=1; i<m; i++) wws[i]+=wws[i-1];    for(i=n-1; i>=0; i--) sa[--wws[x[i]]]=i;    for(j=1,p=1; p<n; j*=2,m=p)    {        for(i=n-j,p=0; i<n; i++) y[p++]=i;        for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;        for(i=0; i<n; i++) wv[i]=x[y[i]];        for(i=0; i<m; i++) wws[i]=0;        for(i=0; i<n; i++) wws[wv[i]]++;        for(i=1; i<m; i++) wws[i]+=wws[i-1];        for(i=n-1; i>=0; i--) sa[--wws[wv[i]]]=y[i];        for(swap(x,y),p=1,i=1,x[sa[0]]=0; i<n; i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;    }    return ;}void gethei(int n){    int i,j,k=0;    for(i=1; i<=n; i++) ra[sa[i]]=i;    for(i=0; i<n; i++)    {        if(k) k--;        j=sa[ra[i]-1];        while(v[i+k]==v[j+k])            k++;        height[ra[i]]=k;    }    return ;}char fuck[12345];int dp[N][30];void rmqinit(int n){    int m=Log[n];    int i,j;    for(i=1; i<=n; i++) dp[i][0]=height[i];    for(i=1; i<=m; i++)    {        for(j=1; j+(1<<i)-1<=n; j++)            dp[j][i]=min(dp[j][i-1],dp[j+(1<<i>>1)][i-1]);    }    return ;}int lcp(int a,int b){    a=ra[a];    b=ra[b];    if(a>b) swap(a,b);    a++;    int m=Log[b-a+1];    return min(dp[a][m],dp[b-(1<<m)+1][m]);}int main(){    int t,i;    Log[0]=-1;    for(i=1; i<=N; i++)        Log[i]=(i&(i-1))?Log[i-1]:Log[i-1]+1;    cin>>t;    while(t--)    {        scanf("%s",fuck);        int n=strlen(fuck);        for(i=0; i<n; i++) v[i]=fuck[i];        v[n]=0;        da(n+1,550);        gethei(n);        rmqinit(n);        int q;        cin>>q;        while(q--)        {            int x,y;            scanf("%d%d",&x,&y);            int ans=(y-x+1)*(1+y-x+1)/2;  //总的长度            int tep[2234];            for(i=0; i<=n; i++) tep[i]=0;  //标记清零            for(i=x-1; i<=y-1; i++) tep[ra[i]]=1;  //标记哪些出现过            int last=-1;  //因为第一个不算 标记是否是第一个            int d=0;      //d表示在当前位置之前的最长的公共子串长度            for(i=1; i<=n; i++)  //遍历sa            {                if(tep[i])                {                    if(last!=-1)                    {                        int z=lcp(sa[last],sa[i]);                        d=min(d,z);       //对于所能提供的长度,d和lcp取最小                        d=max(d,min(z,y-sa[last]));  //对于最长的长度,取d与(lcp与前一个后缀长度的最小值)的最大值                        ans-=min(d,y-sa[i]);        //重复的则是 d与当前长度的最小值                    }                    last=i;                }            }            printf("%d\n",ans);        }    }    return 0;}


0 0