小编典典

检查一个列表是否是另一个重复的列表的轮换

algorithm

我有此功能,以确定一个列表是否是另一个列表的旋转:

def isRotation(a,b):
  if len(a) != len(b):
    return False

  c=b*2
  i=0

  while a[0] != c[i]:
    i+=1

  for x in a:
    if x!= c[i]:
      return False
    i+=1

  return True

例如

>>> a = [1,2,3]
>>> b = [2,3,1]
>>> isRotation(a, b)
True

我该如何处理重复项?例如

a = [3,1,2,3,4]
b = [3,4,3,1,2]

可以及时完成O(n)吗?


阅读 228

收藏
2020-07-28

共1个答案

小编典典

您可以使用最大后缀算法的修改版在0(n)时间和0(1)空间上做到这一点:

摘自《弦论之珠宝》
单词的循环相等

长度为n的单词u的旋转是形式为u [k +1 … n] [l … k]的任何单词。令u,w为两个相同长度n的单词。如果对于某些i,j
u(i)== w(j),则称它们为循环等效的。

如果单词u和w被写为圆圈,则在适当的旋转之后如果圆圈重合,则它们在循环上等效。

有几种线性时间算法可以测试两个单词的循环等效性。最简单的方法是将任何字符串匹配算法应用于模式pat = u和text =
ww,因为如果pat出现在文本中,则单词u和w循环=等效。

另一种算法是找到uu和ww的最大后缀,并检查它们在大小n的前缀上是否相同。我们之所以选择这个问题,是因为有一种更简单有趣的算法,它同时在线性时间和恒定空间中工作,值得介绍。

Algorithm Cyclic-Equivalence(u, w)
{ checks cyclic equality of u and w of common length n }
    x := uu; y := ww;
    i := 0; j := 0;
    while (i < n) and (j < n) do begin
        k := 1;
        while x[i + k] = y[j + k] do k := k + 1;
        if k > n then return true;
        if x[i + k]> y[i + k] then i := i + k else j := j + k;
        { invariant }
    end;
    return false;

哪个翻译成python变成:

def cyclic_equiv(u, v):
    n, i, j = len(u), 0, 0
    if n != len(v):
        return False
    while i < n and j < n:
        k = 1
        while k <= n and u[(i + k) % n] == v[(j + k) % n]:
            k += 1
        if k > n:
            return True
        if u[(i + k) % n] > v[(j + k) % n]:
            i += k
        else:
            j += k
    return False

运行一些示例:

In [4]: a = [3,1,2,3,4]   
In [5]: b =[3,4,3,1,2]   
In [6]: cyclic_equiv(a,b)
Out[6]: True    
In [7]: b =[3,4,3,2,1]    
In [8]: cyclic_equiv(a,b)
Out[8]: False    
In [9]: b =[3,4,3,2]    
In [10]: cyclic_equiv(a,b)
Out[10]: False
In [11]: cyclic_equiv([1,2,3],[1,2,3])
Out[11]: True
In [12]: cyclic_equiv([3,1,2],[1,2,3])
Out[12]: True

更幼稚的方法是使用collections.deque旋转元素:

def rot(l1,l2):
    from collections import deque
    if l1 == l2:
        return True
    # if length is different we cannot get a match
    if len(l2) != len(l1):
        return False
    # if any elements are different we cannot get a match
    if set(l1).difference(l2):
        return False
    l2,l1 = deque(l2),deque(l1)
    for i in range(len(l1)):
        l2.rotate() # l2.appendleft(d.pop()) 
        if l1 == l2:
            return True
    return False
2020-07-28