字符串----不可重叠的最长重复子串

题目:给定一个字符串,求最长重复子串,这两个子串不能重叠。例如,str = "acdcdcdcd",则不可重叠的最长子串为"cdcd"。

思路:二分枚举+height数组分组。这道题的思想很巧妙,后面要仔细推敲。先二分答案,把题目变成判定性问题:判断是否存在两个长度为k的子串是相同的,且不重叠。解决这个问题的关键还是利用height数组。把排序后的后缀分成若干组,其中每组的后缀之间的height值都不小于k。例如,字符串为“aabaaaab”,当k=2时,后缀分成了4组,如图所示。

  

  容易看出,有希望成为最长公共前缀不小于k的两个后缀一定在同一组。然后对于每组后缀,只须判断每个后缀的sa值的最大值和最小值之差是否不小于k。如果有一组满足,则说明存在,否则不存在。整个做法的时间复杂度为O(nlogn)。

代码:

 

 1 public class MaxRepeatSubString2 {
 2 
 3     public static void main(String[] args) {
 4         int res = maxRepeatSubString2("1x23231923263");
 5         System.out.println(res);  // 输出 3
 6     }
 7     
 8     /**
 9      * 不允许交叉
10      * 
11      * @param src
12      * @return
13      */
14     public static int maxRepeatSubString2(String src) {
15         SuffixArray.Suff[] sa = SuffixArray.getSa2(src);
16         int[] height = SuffixArray.getHeight(src, sa);
17         int l = 0;
18         int r = height.length;
19         int ans = 0;
20         while (l <= r) {
21             int mid = l + ((r - l) >> 1);// check的重叠长度
22             if (check(height, sa, mid)) {
23                 if (mid == height.length / 2) {
24                     return mid;
25                 }
26                 l = mid + 1;
27                 ans = mid;
28                 // return mid;
29             } else {
30                 r = mid - 1;
31             }
32         }
33         return ans;
34     }
35     
36     /**
37      * 用len将height分组,小于组和大于等于组交替
38      * 在大于组中更新最大最小原始小标,大转小的时候检查上一个大于组是否满足不重叠
39      * 在小于组中,只需持续地将原始下标付给max和min,这样小转大的时候,可以保留小于组最后一个元素的下标
40      */
41     private static boolean check(int []height,SuffixArray.Suff[]sa,int len){
42         int minIndex = sa[0].index;
43         int maxIndex = sa[0].index;
44         for(int i = 1;i<height.length;i++){
45             int index = sa[i].index;
46             if(height[i]>=len){ // lcp 大于 len
47                 minIndex = Math.min(minIndex,index);
48                 maxIndex = Math.max(maxIndex, index);
49             } else {
50                 if (maxIndex - minIndex >= len) {
51                     return true;
52                 }
53                 maxIndex = index;
54                 minIndex = index;
55             }
56         }
57         return (maxIndex - minIndex) >= len;
58     }
59 
60 }

   在此基础上稍加改动可以完成至少出现K次的最长重复子串(可重叠)的题目

posted @ 2019-01-27 22:17  |旧市拾荒|  阅读(2362)  评论(0编辑  收藏  举报