http://acm.hdu.edu.cn/showproblem.php?pid=4691
去年暑假多校赛的题,当时还不会后缀数组
现在会了,其实自己组合后缀数组跟rmq还是对的,但是题意理解有问题,于是折腾了很久,,,,
此处简单解释下题目样例吧,希望对读者有帮助 以最后一组数据为例
myxophytamyxopodnabnabbednabbingnabit
6
0 9
9 16
16 19
19 25
25 32
32 37
前两行不解释,题目叙述很清楚
从第三行,0 9 指的是第一个字符串是从第一行的字符串的0-9 左闭右开,
以下5行相同
继续看题目的正文叙述的样例
那么压缩之后的第一个就是“0空格以及前9个字符外加一个换行”一共12个,以下几行同样的算法
注意如果公共前缀长度是24,那么按两个单元存储,这就是我写的Weishu函数的作用
上代码:
#include <cstdio> #include <cstring> #include <iostream> #include <cstdlib> #include <algorithm> #include <cmath> using namespace std; const int MAXN =100000+20; int n,k;//n=strlen(s); int Rank[MAXN],tmp[MAXN],d[MAXN],st[MAXN][20],lcp[MAXN],sa[MAXN]; char s[MAXN]; /*使用Rank对sa排序*/ bool cmpSa(int i, int j) { if(Rank[i] != Rank[j])return Rank[i] < Rank[j]; else { /*下面的Rank[t],已经是以t开头长度小于等于k/2的, sa[i]的名次,只是以i开头的后缀,而长度不同*/ int ri = i+k <=n? Rank[i+k]:-1; int rj = j+k <= n ? Rank[j+k]:-1; return ri <rj; } } /*计算SA*/ void consa(char *s, int *sa) { for(int i=0;i<=n;i++){ sa[i]=i;Rank[i] = i < n?s[i]:-1; } for(k=1;k<=n;k*=2)/*注意此代码中k是全局变量 别乱用,循环必须从1开始,因为0*2=0*/ { sort(sa,sa+n+1,cmpSa); tmp[sa[0]] = 0; /*此时tmp只是暂存rank*/ for(int i=1;i<=n;i++){ tmp[sa[i]] = tmp[sa[i-1]] +(cmpSa(sa[i-1],sa[i])?1:0); } for(int i=0;i<=n;i++){ Rank[i] = tmp[i]; } } } void construct_lcp(char *s,int *sa,int *lcp) { //n=strlen(s); for(int i=0; i<=n; i++)Rank[sa[i]]=i; int h=0; lcp[0]=0; for(int i=0;i<n;i++) { int j=sa[Rank[i]-1]; if(h>0)h--; for(; j+h<n && i+h<n; h++) { if(s[j+h]!=s[i+h])break; } lcp[Rank[i]-1]=h; } } void InitRMQ(int nn) { int i,j; for(d[0]=1,i=1;i<21;i++)d[i]=2*d[i-1]; for(i=0;i<nn;i++)st[i][0]=lcp[i]; /////////////////////////////// // for(int i=0;i<nn;i++) //{ // printf("%s i=%d sa=%d rank=%d lcp=%d\n",s+sa[i],i,sa[i],Rank[i],lcp[i]); // printf("||||||||%s lcp[rank]=%d\n",s+i,lcp[Rank[i]]); // } //////////////////////////////// int k=int( log(double(nn))/log(2.0)+1 ); for(j=1;j<k;j++) for(i=0;i<nn;i++) { if(i+d[j-1]-1<nn) { st[i][j]=min(st[i][j-1],st[i+d[j-1]][j-1]); } else break; } } int weishu(int n) { if(n<10)return 1; int ans=0; while(n) { n/=10; ans++; } return ans; } int main() { //freopen("hdu4691.txt","r",stdin); long long ansb,ansa; int kk; while(~scanf("%s",s)) { ansb=ansa=0; n=strlen(s); consa(s,sa); construct_lcp(s,sa,lcp); InitRMQ(n+1); int num,l,r,x,y; scanf("%d",&num); int last=0,lastlen=0; scanf("%d%d",&l,&r); ansb+=r-l+1; ansa+=r-l+3; lastlen=r-l; last=l; for(int i=1;i<num;i++) { scanf("%d%d",&l,&r); ansb+=r-l+1; ansa+=r-l; x=min(Rank[last],Rank[l]),y=max(Rank[last],Rank[l])-1; kk = int( log(double(y-x+1))/log(2.0) ); int ret; if(l == last)ret=n-l; else ret=min(st[x][kk], st[y-d[kk]+1][kk]) ; ret=min(ret, min(lastlen,r-l)); ansa =ansa-ret+weishu(ret)+2; last=l; lastlen=r-l; } printf("%I64d %I64d\n",ansb,ansa); } return 0; }
再加一个rmq+后缀数组求最长公共前缀的模板吧
(其实还没有测试,遇到题在测试)
#include <cstdio> #include <cstring> #include <iostream> #include <cstdlib> #include <algorithm> #include <cmath> using namespace std; const int MAXN =100000+20; int n,k;//n=strlen(s); int Rank[MAXN],tmp[MAXN],d[MAXN],st[MAXN][20],lcp[MAXN],sa[MAXN]; char s[MAXN]; /*使用Rank对sa排序*/ bool cmpSa(int i, int j) { if(Rank[i] != Rank[j])return Rank[i] < Rank[j]; else { /*下面的Rank[t],已经是以t开头长度小于等于k/2的, sa[i]的名次,只是以i开头的后缀,而长度不同*/ int ri = i+k <=n? Rank[i+k]:-1; int rj = j+k <= n ? Rank[j+k]:-1; return ri <rj; } } /*计算SA*/ void consa(char *s, int *sa) { for(int i=0;i<=n;i++){ sa[i]=i;Rank[i] = i < n?s[i]:-1; } for(k=1;k<=n;k*=2)/*注意此代码中k是全局变量 别乱用,循环必须从1开始,因为0*2=0*/ { sort(sa,sa+n+1,cmpSa); tmp[sa[0]] = 0; /*此时tmp只是暂存rank*/ for(int i=1;i<=n;i++){ tmp[sa[i]] = tmp[sa[i-1]] +(cmpSa(sa[i-1],sa[i])?1:0); } for(int i=0;i<=n;i++){ Rank[i] = tmp[i]; } } } void construct_lcp(char *s,int *sa,int *lcp) { //n=strlen(s); for(int i=0; i<=n; i++)Rank[sa[i]]=i; int h=0; lcp[0]=0; for(int i=0;i<n;i++) { int j=sa[Rank[i]-1]; if(h>0)h--; for(; j+h<n && i+h<n; h++) { if(s[j+h]!=s[i+h])break; } lcp[Rank[i]-1]=h; } } void InitRMQ(int nn) { int i,j; for(d[0]=1,i=1;i<21;i++)d[i]=2*d[i-1]; for(i=0;i<nn;i++)st[i][0]=lcp[i]; int k=int( log(double(nn))/log(2.0)+1 ); for(j=1;j<k;j++) for(i=0;i<nn;i++) { if(i+d[j-1]-1<nn) { st[i][j]=min(st[i][j-1],st[i+d[j-1]][j-1]); } else break; } } int ansLcp(int a, int b) { int kk; if(a == b)return n-a;//n是整个字符串的长度 x=min(Rank[a],Rank[b]),y=max(Rank[a],Rank[b])-1; kk = int( log(double(y-x+1))/log(2.0) ); return min(st[x][kk], st[y-d[kk]+1][kk]) ; } int Query(int Q)//Q次询问 { int ans; for(int i=0;i<Q;i++) { scanf("%d%d",&a,&b); ans=ansLcp(a,b); } }