寻找一个字符串中的最长重复子串(后缀数组)
因为工作场景,需要对字符串中重复的子串进行删除操作,那么最重要的过程就是找到最长重复字串,网上有很多暴力方法,我觉得没什么意思,同事也说可以使用正则,然而我不会,所以还是用拿手的java代码解决吧。(本文参考:https://blog.csdn.net/u012114090/article/details/81669021)
后缀数组其实可以看寻找一个字符串中的最长重复子串(后缀数组)作一个由字符串s倒数i个字符组成的子串的集合,其中0<i<s.length(),例如 字符串strstr的后缀数组为:
{r,tr,str,rstr,trstr,strstr}
得到这个集合之后,我们可以发现,最长重复子串其实变成了求该集合中任意两个元素的最长公共前缀的问题。因为在这个集合中,原字符串中的每个字符都会出现在集合中元素的首位,而如果有子串重复出现,那么该子串肯定会出现在某两个或者多个元素的公共前缀上。
接下来问题就变成了如何去寻找最长公共前缀,除了暴力搜素之外,我们可以考虑先对该集合中的元素按照字典序进行排序,然后依次比较相邻的元素,找出其中的最长公共前缀即可。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @Description TODO
* @Author zhangzhishun
* @Date 2020/8/12-12:31
*/
public class Main {
public static void main(String[] args) {
String strTestA = "aabbccedbbccea";
System.out.println(getLongestCommonSubstr(strTestA));
}
public static String getLongestCommonSubstr(String str) {
if (str == null || str.length() == 0) {
return "";
}
// 存放子串
List<String> substrLst = new ArrayList<>();
for (int i = 0; i < str.length(); i++) {
substrLst.add(str.substring(i));
}
// 按着字典序升序排序 归并排序时间复杂度:O(nlogn)
Collections.sort(substrLst);
String result = "";
for (int i = 0; i < substrLst.size() - 1; i++) {
// 比较相邻的元素,找出其中的最长公共前缀
if (getLongestCommonPrefix(substrLst.get(i), substrLst.get(i + 1)).length() > result.length()) {
result = getLongestCommonPrefix(substrLst.get(i), substrLst.get(i + 1));
}
}
return result;
}
/**
* 找出两个字符串的最长公共前缀
*/
public static String getLongestCommonPrefix(String str1, String str2) {
if (str1 == null || str2 == null || str1.length() == 0 || str2.length() == 0) {
return "";
}
int i = 0;
while (i < Math.min(str1.length(), str2.length())) {
if (str1.charAt(i) != str2.charAt(i)) {
break;
}
i++;
}
return str1.substring(0, i);
}
}
分析时间复杂度:
- 生成后缀数组
O(N)
- 归并排序
O(nlogn)
- 依次检测相邻的两个字符串
O(N * N)