題目:給定一個字符串,求最長重複子串,這兩個子串不能重疊。例如,str = "acdcdcdcd",則不可重疊的最長子串爲"cdcd"。數組
思路:二分枚舉+height數組分組。這道題的思想很巧妙,後面要仔細推敲。先二分答案,把題目變成斷定性問題:判斷是否存在兩個長度爲k的子串是相同的,且不重疊。解決這個問題的關鍵仍是利用height數組。把排序後的後綴分紅若干組,其中每組的後綴之間的height值都不小於k。例如,字符串爲「aabaaaab」,當k=2時,後綴分紅了4組,如圖所示。spa
容易看出,有但願成爲最長公共前綴不小於k的兩個後綴必定在同一組。而後對於每組後綴,只須判斷每一個後綴的sa值的最大值和最小值之差是否不小於k。若是有一組知足,則說明存在,不然不存在。整個作法的時間複雜度爲O(nlogn)。3d
代碼:code
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次的最長重複子串(可重疊)的題目blog