一、问题描述
给定一个字符串,求出其最长重复子串
例如:abcdabcd ,最长重复子串是 abcd
最长重复子串可以重叠
例如:abcdabcda ,这时最长重复子串是 abcda ,中间的 a 是被重叠的。
二、分析
1、直观的解法
首先检测长度为 n - 1 的字符串情况,如果不存在重复则检测 n - 2, 一直递减下去,直到 1 。
这种方法的时间复杂度是 O(N * N * N),其中包括三部分,长度纬度、根据长度检测的字符串数目、字符串检测。
2、使用后缀数组
后缀数组:例如对于字符串String str="banana"的后缀数组是a[0]="anana",a[1]="nana",a[2]="ana",a[3]="na",a[4]="a",
改进的方法是利用后缀数组:对一个字符串生成相应的后缀数组后,然后再排序,排完序依次检测相邻的两个字符串的开头公共部分。
这样的时间复杂度为:
1)生成后缀数组 O(N)
2)排序 O(NlogN*N) 最后面的 N 是因为字符串比较也是 O(N)
3)依次检测相邻的两个字符串 O(N * N)
总的时间复杂度是 O(N^2*logN)
三、java实现
分为三步:
第一步:初始化后缀数组
第二步:对后缀数组进行按字典排序
第三步:对后缀数组中相邻元素进行比较,求最长子串
public class MaxSameString { /* * 比较两个相邻的字符串 */ public int sameLen(String str1,String str2){ int index=0; while(index<str1.length() && index<str2.length()){ if(str1.charAt(index)==str2.charAt(index)){ index++; }else { break; } } return index; } /* * 使用快速排序,对字符串按照字典顺序排列 */ public void quickSort(String[] after,int start,int end){ int low=start; int high=end; String pkey=after[low]; while(low<high){ while(low<high && after[high].compareTo(pkey)>0) high--; after[low]=after[high]; while(low<high && after[low].compareTo(pkey)<0) low++; after[high]=after[low]; } after[low]=pkey; if(start<end){ quickSort(after, low+1, end); quickSort(after, start, low-1); } } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub MaxSameString mss=new MaxSameString(); String src="banana";//待处理数组 String[] after=new String[src.length()-1];//后缀数组 //第一步:初始化后缀数组 for(int i=0;i<after.length;++i){ after[i]=src.substring(i+1); } //第二步:对后缀数组进行按字典排序 mss.quickSort(after, 0,after.length-1); int maxlen=0; int maxi=0;//最长字符串出现在后缀数组中的位置 //第三步:对后缀数组中相邻元素进行比较,求最长子串 for(int i=0;i<after.length-1;++i){ int tmp=mss.sameLen(after[i], after[i+1]); if(tmp>maxlen){ maxlen=tmp; maxi=i; } } System.out.println("Max length is "+maxlen); } }