如果我有一个长度为n的字符串S和一个元组列表(a,b),其中a指定S的子字符串的起始位置,b是子字符串的长度。为了检查是否有任何子串重叠,我们可以,例如,只要触摸它就标记S中的位置。但是,如果元组列表的大小为n(循环元组列表,然后循环S),我认为这将花费O(n ^ 2)时间。
是否可以在O(n)时间内检查是否有任何子串实际上与另一个子串重叠?
编辑: 例如,S =" abcde"。元组= [(1,2),(3,3),(4,2)],代表" ab"," cde"和" de"。我想知道在读取(4,2)时会发现重叠。
我认为它是O(n ^ 2)因为你每次都得到一个元组,然后你需要遍历S中的子字符串以查看是否有任何字符标记为脏。
编辑2: 一旦检测到碰撞,我就无法退出。想象一下,我需要报告所有碰撞的后续元组,所以我必须遍历整个元组列表。
编辑3: 算法的高级视图:
for each tuple (a,b)
for (int i=a; i <= a+b; i++)
if S[i] is dirty
then report tuple and break //break inner loop only
答案 0 :(得分:1)
您的基本方法是正确的,但您可以优化您的停止条件,以确保在最坏的情况下有限的复杂性。以这种方式思考 - 在最坏的情况下,你需要在S中有多少个位置进行遍历和标记?
如果没有碰撞,那么在最坏的情况下你会访问长度(S)位置(到那时用完元组,因为任何额外的元组都必须碰撞)。如果发生碰撞 - 您可以停在第一个标记的对象上,所以再次受到未标记元素的最大数量的限制,即长度(S)
编辑:既然您添加了报告所有碰撞元组的要求,让我们再次计算(扩展我的评论) -
标记所有元素后,您可以通过单个步骤(O(1))检测每个其他元组的碰撞,因此您需要O(n + n)= O(n)。 这一次,每个步骤要么标记一个未标记的元素(在最坏的情况下为总体n),要么标识一个碰撞的元组(我们假设的最差的O(元组)也是n)。
实际步骤可以是交错的,因为元组可以以任何方式组织而不首先发生碰撞,但是一旦它们发生(在最多n个元素之后覆盖所有n个元素之后,第一次碰撞之前),你必须碰撞每次都在第一步。其他安排可能会在标记所有元素之前更早地发生碰撞,但是再次 - 你只是重新安排相同数量的步骤。
最坏的情况示例:一个元组覆盖整个数组,然后是n-1个元组(无关紧要) - [(1,n),(n,1),(n-1,1),...(1,1)]
第一个元组将采用n个步骤来标记所有元素,其余的将采用O(1)来完成。总体O(2n)= O(n)。现在说服自己以下示例采用相同数量的步骤 -
[(1,n / 2-1),(1,1),(2,1),(3,1),(n / 2,n / 2),(4,1),(5 ,1)......(n,1)]
答案 1 :(得分:1)
根据你的描述和评论,重叠问题可能不是关于字符串算法,它可以被视为&#34;段重叠&#34;问题
只需使用您的示例,它可以转换为3段:[1,2],[3,5],[4,5]。问题是检查3个部分是否有重叠。
假设我们有m
个段,每个段都有格式[start,end],这意味着段开始位置和结束位置,一个有效的检测重叠的算法是按起始位置按升序排序,需要{{ 1}}。然后迭代已排序的O(m * lgm)
段,对于每个段,尝试查找其结束位置,在这里您只需要检查:
m
每次检查操作需要if(start[i] <= max(end[j], 1 <= j <= i-1) {
segment i is overlap;
}
maxEnd[i] = max(maxEnd[i-1], end[i]); // update max end position of 1 to i
。然后,总时间复杂度为O(1)
,可以将其视为O(m*lgm + m)
。对于每个输出,时间复杂度与每个元组的长度相关,这也与O(m*lgm)
有关。
答案 2 :(得分:0)
这是一个段重叠问题,如果元组列表已按第一个字段的升序排序,则该解决方案应该可以在O(n)
本身。请考虑以下方法:
将间隔从(开始,字符数)转换为(start,inclusive_end)。因此,上面的示例变为:[(1,2),(3,3),(4,2)] ==> [(1, 2), (3, 5), (4, 5)]
如果转换后的连续元组(a, b)
和(c, d)
始终跟随b < c
,则元组有效。否则,上面提到的元组有重叠。
如果数组按上述形式排序,1
和2
中的每一个都可以在O(n)
中完成。