后缀数组应用

       求两个后缀数组的最长公共前缀,可以转化为求某个区间上的最小值。 接下来,先来构造后缀数组,先看这里    

//求两个后缀的最长公共前缀 <1>
int lcs(int i, int j){
  int b, e;
  if(RANK[i] > RANK[j])
    e=RANK[i], b=RANK[j]+1;
  else e=RANK[j], b=RANK[i]+1;
  int lcs=height[b++];
  for(; b<=e; ++b) lcs=min(lcs, height[b]);
  return lcs;
}

       如果用RMQ 的  Sparse-Tree 预处理,则可以做到 O(nlogn)。

// RMQ Sparse-Tree 预处理
int m=log(len)/log(2.0)+1;	// len是字符串的长度
int dp_min[maxlen][m], dp_max[maxlen][m];
void RMQ_init(){
  int i=0,j=0;
  for(i=1; i<=len; i++)
    dp_max[i][0]=dp_min[i][0]=s[i];
  double k=log(n)/log(2.0);
  for(i=1; i<(int)k; i++)
    for(j=1; j+(1<<i)-1 <= n; i++){
      dp_max[j][i] = max(dp_max[i][j-1], dp_max[ j+(1<<(i-1)) ][i-1]);
      dp_min[j][i] = min(dp_min[i][j-1], dp_min[ j+(1<<(i-1)) ][i-1]);
    }
}
// RMQ :求两个后缀的最长公共前缀
int RMQ_min(int i, int j){
  if(i>j)swap(i,j);
  int k=int(log(j-i)/log(2.0));
  return min(dp_min[i][k], dp_min[ j-(1<<k)+1 ][k]);
}
        求可重叠最长重复子串,等价于求两个后缀的最长公共前缀的最大值,因为任意两个后缀的最长公共前缀都是height数组里的某一段的最小值,那么这个值一定不大于height数组里的最大值,所以,最长公共前缀就是height数组里的最大值了。上面已经用RMQ预处理了,那就利用起来把。

// RMQ: 求可重叠最长重复子串
int RMQ_max(int i, int j){
  if(i>j) swap(i,j);
  int k=int(log(j-i)/log(2.0));
  return max(dp_maxp[i][k], dp_max[ j-(1<<k)+1 ][k]);
}
        求两个字符串的最长公共子串,先将第二个字符串写在第一个字符串后面,中间用一个没有出现过的字符隔开,再求这个新的字符串的后缀数组,在height[i]是两个字符串的公共前缀的前提下,求出height[i]的最大值。

// 最长公共子串
int longest_common_substring(int inter){
  // inter是两串字符串中间的分隔符的位置
  int ans = 0;
  for(int i = 2; i <= len; ++i)
    if((sa[i-1] < inter && sa[i] > inter) ||
       (sa[i] > inter && sa[i-1] > inter))
      ans = max(ans, height[i]);
  // if 判断部分看到别人是这样写的
  // if((sa[i-1] - inter)*(sa[i] - inter) < 0)
  return ans;
}

     参考:罗穗骞 《后缀数组——处理字符串的有力工具》

      



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值