给定一个字符串,输出最长的重复子串
举例:ask not what your country can do for you,but what youcan do for your country
最长的重复子串:can do for you
思路:使用后缀数组解决
分析:
1、由于要求最长公共子序列,则需要找到字符串的所有子串,即通过产生字符串的后缀数组实现。
2、由于要求最长的重复子串,则需要对所有子串进行排序,这样可以把相同的字符串排在一起。
3、比较相邻字符串,找出两个子串中,相同的字符的个数。
注意,对于一个子串,一个与其重复最多的字符串肯定是紧挨着自己的两个字符串。
步骤:
1、对待处理的字符串产生后缀数组
2、对后缀数组排序
3、依次检测相邻两个后缀的公共长度
4、取出最大公共长度的前缀
这里的子串是可以相交的,比如ababa,返回的最长重复子串的长度为3
/*
* 使用后缀数组,具体思路参见http://blog.csdn.net/iamskying/article/details/4759485 不过
* 1.这个思路在生成后缀数组时,用“暴力”生成:先取最后1个元素,然后取最后2个元素、最后3个元素...
* 据说可以通过倍增算法和DC3算法,不过没研究过... 2.下面这个结论我不明白,后缀数组的原理还没研究过-->
* “将所有后缀数组按字典顺序排序后,两个相邻位置的后缀数组比较,取其最长公共前缀,即为所求字符串s的最长可重叠重复子串”
*/
public void findMaxPrefixCross(String str) {
String[] suffixArray = generateSuffixArray(str);
// string sort
List<String> list = (List<String>) Arrays.asList(suffixArray);
Collections.sort(list);
// 两个相邻位置的后缀数组比较,取其最长公共前缀
// int startIndex=0;//只有第一个字符相同时,才有“公共前缀”
int prefixLen = -1;
int index = -1;
for (int i = 0, len = suffixArray.length; i < len - 1; i++) {
char[] str1 = suffixArray[i].toCharArray();
char[] str2 = suffixArray[i + 1].toCharArray();
int k1 = 0, k2 = 0;
int len1 = str1.length, len2 = str2.length;
int count = 0;
if (str1[0] == str2[0]) {// 只有第一个字符相同时,才有“公共前缀”
while (k1 < len1 && k2 < len2 && str1[k1] == str2[k2]) {
k1++;
k2++;
count++;
}
if (count > prefixLen) {
prefixLen = count;
index = i;
}
}
}
if (index != -1) {
String str3 = suffixArray[index];
System.out.println("one of the max repeat SubString is "
+ str3.substring(0, prefixLen));
System.out.println("length=" + prefixLen);
} else {
System.out.println("no duplicate char ");
}
}
public String[] generateSuffixArray(String str) {
int len = str.length();
String[] suffixArray = new String[len];
for (int i = 0; i < len; i++) {
int endIndex = len;
int beginIndex = len - 1 - i;
suffixArray[i] = str.substring(beginIndex, endIndex);
}
return suffixArray;
}