最近我在亚马逊的采访中被问到这个问题。
给定一个字符串,从中删除连续的重复子字符串。如果有多个连续的交叉子串,请删除其中最大的子串。
为清楚起见,以下是一些例子:
aabcccddeaaa
abcdea
(压缩连续重复的字符)abababcdeee
abcde
(压缩连续重复的子串)ababcdabcd
ababcd
(您可以压缩“ab
”或“abcd
”,但由于“abcd
”的长度较大,您更喜欢压缩较大的长度。)
我无法提出有效的实施方案,任何人都知道这方面的好方法吗?
由于这是一个面试问题,请不要使用复杂的图书馆功能。
答案 0 :(得分:2)
对于字符串X
,我们可以使用Z-algorithm Given a string S of length n, the Z Algorithm produces an array Z where Z[i] is the length of the longest substring starting from pat[i] which is also a prefix of pat
(Source)
对于X
的每个后缀,从i
开始,对此子字符串应用Z算法:
int result = 0;
for(int i = 0; i < X.length(); i++)
int[]z = Z-algo(X.substring(i)); //this take O(n)
for(int j = i + result + 1; j < X.length(); j++)
if(z[j] >= j - i + 1)
result = (j - i + 1);
重复上述过程,直到我们找不到任何重复的子串,我们就可以得到一个O(n ^ 3)算法。
注意:重新阅读问题后,特别是最后一个例子,我发现有效的重复子字符串仅限于原始的子字符串。因此,通过使用最大堆,时间复杂度可以减少到O(n ^ 2 log n)。
答案 1 :(得分:1)
没有正则表达式...这种递归方法有效:
var cases = ['aabcccddeaaa', 'abababcdeee', 'ababcdabcd'];
function compress(str) {
var len, sub, i, n;
// if str is shorter than 2, can't be any repeating substrings
if(str.length < 2)
return str;
// max meaningful length is str.length/2 (truncated to integer)
for(len = (str.length / 2) | 0; len > 0; len--) {
// search for a repeating substring of "len" length starting at index i
for(i = 0; i + (len * 2) <= str.length; i++) {
sub = str.substr(i, len);
// if such a substring exists...
if(str.indexOf(sub, i + len) == i + len) {
// "count" how many occurences (advance index till beyond repeats)
for(n = i + len * 2; str.indexOf(sub, n) == n; n += len);
// return a string composed of the compressed part before the match +
// the substring + the compressed part beyond the match
return compress(str.substr(0, i)) + sub + compress(str.substr(n));
}
}
}
// if nothing found, return original string
return str;
}
alert(JSON.stringify(cases.map(compress)));
在关于算法复杂性的评论中经过长时间的争论后,我决定重构一下并使用自我实现的startsWith
函数,并利用它来计算内部操作(复杂性......)。 / p>
我抓住机会通过最小化字符串分配来进一步优化,所以现在递归适用于整个字符串+开始/结束索引。
下面的代码生成一个输出,其中包括输入字符串,结果,n ^ 2(用于O(n ^ 2)比较)和实际内部操作计数。我添加了一些边缘情况来展示它的表现。我找不到导致n ^ 2计数的输入,它们都在下面。
var cases = ['aabcccddeaaa', 'abababcdeee', 'ababcdabcd',
'aabaaabaab', '1', '111222', '123456789', '1234567899'];
var innerCount;
function startsWith(str, start, subStart, subLen) {
var subEnd = subStart + subLen - 1;
while(subStart <= subEnd) {
innerCount++;
if(str[start++] != str[subStart++])
return false;
}
return true;
}
function doCompress(str, maxLen, minIndex, maxIndex) {
var len, sub, i, n;
// if str is shorter than 2, can't be any repeating substrings
if(maxIndex - minIndex + 1 < 2)
return str.substring(minIndex, maxIndex + 1);
for(len = maxLen; len > 0; len--) {
// search for a repeating substring of "len" length starting at index i
for(i = minIndex; i + (len * 2) <= maxIndex + 1; i++) {
// if such a substring exists...
if(startsWith(str, i + len, i, len)) {
// "count" how many occurences (advance index till beyond repeats)
for(n = i + len * 2; (n + len <= maxIndex + 1) && startsWith(str, n, i, len); n += len);
// return a string composed of the compressed part before the match +
// the substring + the compressed part beyond the match
return (i > minIndex ? doCompress(str, len - 1, minIndex, i - 1) : '') +
str.substr(i, len) +
(n < maxIndex ? doCompress(str, len, n, maxIndex) : '');
}
}
}
// if nothing found, return original string
return str.substring(minIndex, maxIndex + 1);
}
function compress(str) {
innerCount = 0;
// max meaningful length is str.length/2 (truncated to integer)
return {
source: str,
result: doCompress(str, (str.length / 2) | 0, 0, str.length - 1),
'n^2': str.length*str.length,
innerCount: innerCount};
}
alert(JSON.stringify(cases.map(compress), null, '\t'));
此解决方案的时间复杂度为O(n ^ 2)。
答案 2 :(得分:1)
pos=[]
dstr={}
final=[]
x="ababcdabcdcde"
for k in re.finditer(r"(?=(.+?)\1+)",x): #Find start of all overlapping strings
pos.append(k.start())
i=0
for k in pos: #Find end of overlapping strings
s=re.findall(r"^((.*)\2+)",x[k:])
dstr[i]=(k,len(s[0][0]))
i=i+1
#print dstr.values()
k=0
while k< len(dstr.values())-1: #remove smaller length overlapping result
if dstr.values()[k+1][0]<dstr.values()[k][1]<dstr.values()[k+1][1]:
pass
else:
final.append(dstr.values()[k][0])
k=k+1
if dstr.values()[k-1][0] in final:
pass
else:
final.append(dstr.values()[k][0])
#print final
for k in final: #remove strings
s=re.sub(r"(.*)\1+",r"\1",x[k:])
x=x[:k]+s
print x
对于给定的输入,这在python.Works中很好。
答案 3 :(得分:0)
有一个简单的非递归O(n ^ 3)。关键的观察是:假设有一个字符串&#39; aabcbcabbc&#39;,如果我们只是删除连续重复,只要我们首先减少长度= 1的字符串,长度= 2秒,依此类推,我们可以减少它,减少将是最佳的。因此
&#39; aabcabbc&#39; =&GT; &#39; abcbcabc&#39; =&GT; &#39; ABCABC&#39; =&GT; &#39; ABC&#39;
Python代码:
def strcompress(str):
strlen = len(str)
for size in range (1, strlen // 2):
for i in range (0, strlen - 2 * size + 1):
str1 = str[i:i+size]
str2 = str[i+size:i+2*size]
while str1 == str2:
str = str[:i+size] + str[i+2*size:]
strlen = len(str)
if i + 2*size > strlen:
break
str2 = str[i+size:i+2*size]
print("The compressed string is:" + str)
return
示例:
>>> strcompress("ababcdabcd")
The compressed string is:abcd
编辑:修复了代码中的一些错误。这适用于现有样本和我提供的示例。