四年之痒之求解最长重复子串
四年前,校园招聘,金山一道笔试题。
求一个字符串中的最长重复子串,比如:"xxxxxxzuichangzichuanyyyyyyyzuichangzichuan",最长重复子串应该是"zuichangzichuan"。
四年后,我的答案:
(function(str){ str = str || ''; if(str.length == 1) return str; var max_str_start = 0, max_str_end = 0, max_str_length = 0; for(var startIndex = 0, len = str.length - 1; startIndex < len && (len - startIndex -1 > max_str_length); startIndex++){ for(var endIndex = str.length - 1; (startIndex < endIndex) && (endIndex - startIndex > max_str_length); endIndex--){ var sub_str = str.substring(startIndex, endIndex); if(str.lastIndexOf(sub_str) > startIndex){ max_str_start = startIndex; max_str_end = endIndex; max_str_length = endIndex - startIndex; break; } } } var max_str = str.substring(max_str_start, max_str_end); console.log('max string: ' + max_str + ', max string start: ' + max_str_start + ', max string end: ' + max_str_end); return max_str; })("xxxxxxzuichangzichuanyyyyyyyzuichangzichuan");
思路:
定义三个变量,最大字串的开始位置startIndex,结束位置endIndex,字符串长度为len。
以最极端的情况入手,假设最大子串为字符串前(len-1)位,即
startIndex = 0, endIndex = len -1;
那么最大子串为
sub_str = str.substring(startIndex, endIndex)
然后查找原始字串中是否存在与该子串重复的子串,注意:非该子串本身
str.lastIndexOf(sub_str) > startIndex
如果不存在,endIndex向前移一位后,继续查找
endIndex--
如果存在,则跳出本次循环,即在startIndex为0的最大子串已经找到,startIndex向后移一位,并重置endIndex为len-1,继续查找。
max_str_start = startIndex; max_str_end = endIndex; max_str_offset = endIndex - startIndex; startIndex++
以banana为例:
1. banan
2. bana
3. ban
4. ba
5. b
6. anan
7. ana(发现重复子串,记录位置)
之后就无需再比较了,因为当前字串已经是最长字串了,原因见黄色标记部分。
算法复杂度:
两个极端情况:1,所有字符都相同,只需查找一次;2,所有字符都不同,需查找n(n-1)/2次。
问题:
如果是找出两个字符串中最长公共子串呢?