后缀数组与高度数组的使用(求解最长重复子串)

1. 后缀数组和高度数组联合可以求解最长重复子串(可重叠或者说可交叉)的问题

例如:"123232323"的最长重复子串为232323,因为后缀23232323的前缀为232323与后缀232323的最大重复的元素为232323

所以它的最长重复子串为232323

sa数组的相关情况如下:(sa数组为所有后缀排好名之后的字符串数组,而且还绑定了后缀的下标

字符串                    索引             排名

123232323              0                   0

23                            7                   1

2323                        5                   2

232323                    3                   3

23232323                1                   4

3                              8                   5

323                          6                   6

32323                      4                   7

3232323                  2                   8

 

2. 而高度数组是用来求解某两个排名相近的后缀的最长公共前缀,所以我们可以利用高度数组来求解最长重复子串的问题

高度数组的情况

字符串                                  height[k]                索引

123232323                                 0                       0

23                                               0                       7            

2323                                           2                       5

232323                                       4                       3

23232323                                   6                       1

3                                                 0                       8

323                                             1                       6

32323                                         3                       4

3232323                                     5                       2

从上面可以看出使用求出高度数组中的最大值就可以解决最大重复子串的问题

具体的代码如下:

public class Main {
	public static void main(String[] args) {
		String src = "123232323";
		int res = maxRepeatSubString(src);
		System.out.println(res);
	}
	
	public static int maxRepeatSubString(String src) {
	    Suff[] sa =Suff.getSa(src);
	    int[] height = 高度数组.getHeight(src, sa);
	    int maxHeight = 0;
	    int maxIndex = -1;
	    for (int i = 0; i < height.length; i++) {
	      if (height[i] > maxHeight) {
	        maxHeight = height[i];
	        maxIndex = i;
	      }
	    }
	    int index = sa[maxIndex].index;//转成原始下标
	    //System.out.println(src.substring(index,index+maxHeight));
	    return maxHeight;
	}
}

 

求解高度数组的代码为:

public class Main {
	public static int[] getHeight(String src,Suff sa[]){
		//Suff[] sa =getSa2(src);
	    int strLength = src.length();
	    int[] rk = new int[strLength];
	    //将rank表示为不重复的排名即0~n-1
	    for(int i = 0; i<strLength;i++) {
	      rk[sa[i].index] = i;
	      //System.out.print(sa[i].index+" ");
	    }
	    //System.out.print("\n");
	    int[] height = new int[strLength];
	    // 如果已经知道后缀数组中i与i+1的lcp为h,那么i代表的字符串与i+1代表的字符串去掉首字母后的lcp为h-1.
	    // 根据这个我们可以发现,如果知道i与后缀数组中在它后一个的lcp为k,那么它去掉首字母后的字符串与其在后缀数组中的后一个的lcp大于等于k-1
	    // 例如对于字符串abcefabc,我们知道abcefabc与abc的lcp为3.
	    // 那么bcefabc与bc的lcp大于等于3-1.
	    // 利用这一点就可以O(n)求出高度数组。
	    int k = 0;
	    for(int i = 0; i<strLength;i++) {
	      int rk_i = rk[i];//i后缀的排名
	      //System.out.print(rk_i+" ");
	      if (rk_i == 0) {
	        height[0] = 0;
	        continue;
	      }
	      int rk_i_1 = rk_i - 1;
	      int j = sa[rk_i_1].index;//j是i串字典序靠前的串的下标
	      //System.out.println("j = "+j);
	      if(k>0) k--;
	      for(;j+k<strLength&&i+k<strLength;k++) {
	        if (src.charAt(j+k)!=src.charAt(i+k))
	          break;
	      }
	      height[rk_i] = k;
	      //System.out.println(rk_i+" "+k);
	    }
	    return height;
	  }
	}

最后求出"123232323"的最大重复子串长度为6

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值