给出一个仅包含四个字母['a','g','c','t']
的字符串序列
例如:agggcttttaaaatttaatttgggccc
。
查找字符串序列中所有长度最短的唯一子字符串(长度应为所有唯一子字符串的最小值)?
例如:aaggcgccttt
答案:['aa', 'ag', 'gg','cg', 'cc','ct']
说明:长度为2的最短唯一子字符串
我尝试使用后缀数组和最长的公共前缀,但我无法完美地绘制解决方案。
答案 0 :(得分:0)
我不确定“最小唯一子字符串”是什么意思,但是在您的示例中,我假设您的意思是“单个字母的最短运行时间”。如果是这种情况,您只需要遍历字符串一次(一个字符一个字符)并计算找到的所有最短运行次数。您应该跟踪到目前为止找到的最小运行的长度(开始时为无穷大)和当前运行的长度。
如果您需要找到确切的运行次数,可以将找到的所有最小运行次数添加到例如遍历字符串时列出一个列表(如果发现运行次数较短,则相应地修改该列表)。
编辑: 我对问题进行了更多思考,并提出了以下解决方案。
我们找到所有长度为i的唯一子字符串(按升序)。因此,首先我们考虑长度为1的所有子字符串,然后考虑长度为2的所有子字符串,依此类推。如果找到任何内容,我们将停止,因为子字符串的长度只能从这一点开始增加。
您将不得不使用一个列表来跟踪您到目前为止所看到的子字符串,并使用一个列表来存储实际的子字符串。找到新的子字符串时,还必须相应地维护它们。
这是我想出的Java代码,以备不时之需:
String str = "aaggcgccttt";
String curr = "";
ArrayList<String> uniqueStrings = new ArrayList<String>();
ArrayList<String> alreadySeen = new ArrayList<String>();
for (int i = 1; i < str.length(); i++) {
for (int j = 0; j < str.length() - i + 1; j++) {
curr = str.substring(j, j + i);
if (!alreadySeen.contains(curr)){ //Sub-string hasn't been seen yet
uniqueStrings.add(curr);
alreadySeen.add(curr);
}
else //Repeated sub-string found
uniqueStrings.remove(curr);
}
if (!uniqueStrings.isEmpty()) //We have found non-repeating sub-string(s)
break;
alreadySeen.clear();
}
//Output
if (uniqueStrings.isEmpty())
System.out.println(str);
else {
for (String s : uniqueStrings)
System.out.println(s);
}
uniqueStrings
列表包含最小长度的所有唯一子字符串(用于输出)。 alreadySeen
列表会跟踪已经看到的所有子字符串(用于排除重复的子字符串)。
答案 1 :(得分:0)
我将用Python编写一些代码,因为这是我发现的最简单的方法。 我实际上写了 overlapping 和 non-overlapping 变体。另外,它还会检查输入是否有效。 您似乎只对重叠变体感兴趣:
import itertools
def find_all(
text,
pattern,
overlap=False):
"""
Find all occurrencies of the pattern in the text.
Args:
text (str|bytes|bytearray): The input text.
pattern (str|bytes|bytearray): The pattern to find.
overlap (bool): Detect overlapping patterns.
Yields:
position (int): The position of the next finding.
"""
len_text = len(text)
offset = 1 if overlap else (len(pattern) or 1)
i = 0
while i < len_text:
i = text.find(pattern, i)
if i >= 0:
yield i
i += offset
else:
break
def is_valid(text, tokens):
"""
Check if the text only contains the specified tokens.
Args:
text (str|bytes|bytearray): The input text.
tokens (str|bytes|bytearray): The valid tokens for the text.
Returns:
result (bool): The result of the check.
"""
return set(text).issubset(set(tokens))
def shortest_unique_substr(
text,
tokens='acgt',
overlapping=True,
check_valid_input=True):
"""
Find the shortest unique substring.
Args:
text (str|bytes|bytearray): The input text.
tokens (str|bytes|bytearray): The valid tokens for the text.
overlap (bool)
check_valid_input (bool): Check if the input is valid.
Returns:
result (set): The set of the shortest unique substrings.
"""
def add_if_single_match(
text,
pattern,
result,
overlapping):
match_gen = find_all(text, pattern, overlapping)
try:
next(match_gen) # first match
except StopIteration:
# the pattern is not found, nothing to do
pass
else:
try:
next(match_gen)
except StopIteration:
# the pattern was found only once so add to results
result.add(pattern)
else:
# the pattern is found twice, nothing to do
pass
# just some sanity check
if check_valid_input and not is_valid(text, tokens):
raise ValueError('Input text contains invalid tokens.')
result = set()
# shortest sequence cannot be longer than this
if overlapping:
max_lim = len(text) // 2 + 1
max_lim = len(tokens)
for n in range(1, max_lim + 1):
for pattern_gen in itertools.product(tokens, repeat=2):
pattern = ''.join(pattern_gen)
add_if_single_match(text, pattern, result, overlapping)
if len(result) > 0:
break
else:
max_lim = len(tokens)
for n in range(1, max_lim + 1):
for i in range(len(text) - n):
pattern = text[i:i + n]
add_if_single_match(text, pattern, result, overlapping)
if len(result) > 0:
break
return result
经过一些合理的检查,以检查输出的正确性:
shortest_unique_substr_ovl = functools.partial(shortest_unique_substr, overlapping=True)
shortest_unique_substr_ovl.__name__ = 'shortest_unique_substr_ovl'
shortest_unique_substr_not = functools.partial(shortest_unique_substr, overlapping=False)
shortest_unique_substr_not.__name__ = 'shortest_unique_substr_not'
funcs = shortest_unique_substr_ovl, shortest_unique_substr_not
test_inputs = (
'aaa',
'aaaa',
'aaggcgccttt',
'agggcttttaaaatttaatttgggccc',
)
import functools
for func in funcs:
print('Func:', func.__name__)
for test_input in test_inputs:
print(func(test_input))
print()
Func: shortest_unique_substr_ovl
set()
set()
{'cg', 'ag', 'gg', 'ct', 'aa', 'cc'}
{'tg', 'ag', 'ct'}
Func: shortest_unique_substr_not
{'aa'}
{'aaa'}
{'cg', 'tt', 'ag', 'gg', 'ct', 'aa', 'cc'}
{'tg', 'ag', 'ct', 'cc'}
标定我们实际的速度是明智的。
下面您可以找到一些基准,这些基准是使用here中的一些模板代码生成的(重叠变体为 blue ):
以及其他完整代码:
def gen_input(n, tokens='acgt'):
return ''.join([tokens[random.randint(0, len(tokens) - 1)] for _ in range(n)])
def equal_output(a, b):
return a == b
input_sizes = tuple(2 ** (1 + i) for i in range(16))
runtimes, input_sizes, labels, results = benchmark(
funcs, gen_input=gen_input, equal_output=equal_output,
input_sizes=input_sizes)
plot_benchmarks(runtimes, input_sizes, labels, units='ms')
plot_benchmarks(runtimes, input_sizes, labels, units='μs', zoom_fastest=2)
就渐近时间复杂度分析而言,仅考虑重叠情况,令N
为输入大小,令K
为令牌数(在您的情况下为4),find_all()
为O(N),shortest_unique_substr
的主体为O(K²)
(+ O((K - 1)²) + O((K - 2)²) + ...
)。
因此,总体来说,这是O(N*K²)
或O(N*(Σk²))
(对于k = 1, …, K
),因为K
是固定的,所以基准测试似乎表明,这是O(N)
。 / p>