hdu4691(后缀数组+ST算法)

Front compression

Time Limit: 5000/5000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others)
Total Submission(s): 623    Accepted Submission(s): 255


Problem Description
Front compression is a type of delta encoding compression algorithm whereby common prefixes and their lengths are recorded so that they need not be duplicated. For example:

The size of the input is 43 bytes, while the size of the compressed output is 40. Here, every space and newline is also counted as 1 byte.
Given the input, each line of which is a substring of a long string, what are sizes of it and corresponding compressed output?
 

Input
There are multiple test cases. Process to the End of File.
The first line of each test case is a long string S made up of lowercase letters, whose length doesn't exceed 100,000. The second line contains a integer 1 ≤ N ≤ 100,000, which is the number of lines in the input. Each of the following N lines contains two integers 0 ≤ A < B ≤ length(S), indicating that that line of the input is substring [A, B) of S.
 

Output
For each test case, output the sizes of the input and corresponding compressed output.
 

Sample Input
  
  
frcode 2 0 6 0 6 unitedstatesofamerica 3 0 6 0 12 0 21 myxophytamyxopodnabnabbednabbingnabit 6 0 9 9 16 16 19 19 25 25 32 32 37
 

Sample Output
  
  
14 12 42 31 43 40
 

Author
Zejun Wu (watashi)
 

Source
 

Recommend
zhuyuanchen520
 
本题首先给出一个字符串,然后给定若干区间,除去与前面一个的最长前缀,问最后总的字符串有多长(当然有些小细节就不在此出题了)。
本题可以用后缀数组求出所有后缀彼此间的最长公共前缀,在此基础上进行RMQ,RMQ问题可以用时间复杂度为O(N*log(N))的ST算法(稀疏表法)进行预处理,然后在此基础上每次查询在O(1)时间复杂度下完成。
关于后缀数组的知识可参见 http://blog.csdn.net/xj2419174554/article/details/10053879
关于RMQ问题-ST算法的知识可参见 http://blog.csdn.net/xj2419174554/article/details/10154135
 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;

const int MAXN=100000+100;
char str[MAXN];
int sa[MAXN];
int wa[MAXN],wb[MAXN],wv[MAXN],wh[MAXN];
int cmp(int *r,int a,int b,int l)
{
 return r[a]==r[b]&&r[a+l]==r[b+l];
}
//求后缀数组sa[],下标1到n-1(此处n=strlen(str)+1)有效后缀
//将str的n个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入sa中。
//保证Suffix(sa[i])<Suffix(sa[i+1])
//1<=i<n,sa[0]存放人为添加在末尾的那个最小的后缀
//倍增算法的时间复杂度为O(nlogn)
//倍增算法的空间复杂度都是O(n)
void da(char *r,int *sa,int n,int m)
{
 int i,j,p,*x=wa,*y=wb,*t;
 for(i=0;i<m;i++) wh[i]=0;
 for(i=0;i<n;i++) wh[x[i]=r[i]]++;
 for(i=1;i<m;i++) wh[i]+=wh[i-1];
 for(i=n-1;i>=0;i--) sa[--wh[x[i]]]=i;
 for(j=1,p=1;p<n;j*=2,m=p)
 {
  for(p=0,i=n-j;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++) wh[i]=0;
  for(i=0;i<n;i++) wh[wv[i]]++;
  for(i=1;i<m;i++) wh[i]+=wh[i-1];
  for(i=n-1;i>=0;i--) sa[--wh[wv[i]]]=y[i];
  for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
   x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
 }
 return;
}

int rank[MAXN],height[MAXN];
//定义height[i]=suffix(sa[i-1])和suffix(sa[i])的最长公
//共前缀,也就是排名相邻的两个后缀的最长公共前缀
//任意两个起始位置为i,j(假设rank[i]<rank[j])的后缀的最长公共前缀
//为height[rank[i]+1]、height[rank[i]+2]…height[rank[j]]的最小值
void calheight(char *r,int *sa,int n)
{
 int i,j,k=0;
 for(i=1;i<=n;i++) rank[sa[i]]=i;
 for(i=0;i<n;height[rank[i++]]=k)
  for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
  return;
}


int dp[MAXN][20];
inline int Min(int a,int b)
{
 return a<b?a:b;
}
//预处理过程,时间复杂度为O(N*log(N))
//ST算法求区间最值
//dp[i][j]表示区间[i,i+2^j-1]最值
//求dp[i][j]时将其分成dp[i][j-1],dp[i+2^(j-1)][j-1]
//[i,i+2^j-1]=[i,i+2^(j-1)-1]+[i+2^(j-1),i+2^(j-1)+2^(j-1)-1]
inline void Rmp_ST(int *data,int n)
{
 int l,s;
 for(int i=1;i<=n;i++)
 {
  dp[i][0]=data[i];
 }
 for(l=1;l<=16;l++)
 {
  for(s=1;s<=n;s++)
  {
   if(s+(1<<l)-1<=n)
   {
    dp[s][l]=Min(dp[s][l-1],dp[s+(1<<(l-1))][l-1]);
   }
  }
 }
}

//查询[s,e]区间最值,下标从1-n
//求一个最大的k,是k满足2^k<=e-s+1
//原理:区间[s,e]=区间[s,s+2^k-1]+区间[e-2^k+1,e]
//s<=e-2^k+1,s+2^k-1<=e保证刚好完全覆盖
inline int Query(int s,int e)
{
 int ans,k;
 k=(int)(log(1.0*e-s+1)/log(2.0));
 ans=Min(dp[s][k],dp[e-(1<<(k))+1][k]);
 return ans;
}

//返回起始位置分别为a、b的后缀的最长公共前缀
int lcp(int n,int a,int b)
{
 //a和b为同一后缀与不进行处理,就会返回0
    if(a==b)return n-a;//a和b为同一后缀,直接输出,字串串长度为n-a
 int ra=rank[a],rb=rank[b];
 if(ra>rb)
 {
  int tmp=ra;
  ra=rb;
  rb=tmp;
 }
    return Query(ra+1,rb);
}


int main()
{
 int query,i,k,j;
 
 int w[MAXN];
 w[0]=1;
 for(i=k=1;i<MAXN;i=j,k++)
 {
  for(j=i*10;i<j&&i<MAXN;i++)
   w[i]=k;
 }//预处理数字占的位数
 
 while(~scanf("%s",str))
 {
  int n=strlen(str);
  str[n]='#';
  da(str,sa,n+1,128);
  calheight(str,sa,n);
  Rmp_ST(height,n);
  scanf("%d",&query);
  
  __int64 ans1,ans2;
  int pa,pb,a,b;
  ans1=(__int64)query,ans2=(__int64)query*2;
  scanf("%d%d",&pa,&pb);
  ans1+=(__int64)pb-pa;ans2+=(__int64)pb-pa+1;
  while(--query)
  {
   scanf("%d%d",&a,&b);
   ans1+=(__int64)b-a;
   int t=(__int64)Min(lcp(n,pa,a),pb-pa);
   t=Min(t,b-a);
   ans2+=(__int64)b-a-t+w[t];
   pa=a;pb=b;
  }
  printf("%I64d %I64d\n",ans1,ans2);
  
 }
 return 0;
}







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值