列表的最大后缀

时间:2014-01-28 15:19:23

标签: algorithm

此问题试图找到给定列表的词典编码最大后缀。


假设我们有一个数组/列表[e1; e2; e3; e4; e5]。

然后[e1; e2; e3; e4; e5]的所有后缀都是:

[E1; E2; E3; E4; E5]
[E2; E3; E4; E5]
[E3; E4; E5]
[E4; E5]
[e5]

然后,我们的目标是在上述 5 列表中找到词典编辑最多的一个。


例如,[1; 2; 3; 1; 0]的所有后缀都是

[1; 2; 3; 1; 0]
[2; 3; 1; 0]
[3; 1; 0]
[1; 0]
[0]。

上面的例子中的词典编写最大后缀为[3;1;0]


直接算法只是逐个比较所有后缀并始终记录最大值。比较两个需要O(n^2)的列表时,时间复杂度为O(n)

但是,所需的时间复杂度为 O(n),并且不应使用后缀树(也没有后缀数组)

请注意,列表中的元素可能不是明显的

2 个答案:

答案 0 :(得分:2)

int max_suffix(const vector<int> &a) 
{
  int n = a.size(), 
      i = 0, 
      j = 1, 
      k;

  while (j < n) 
  {
    for (k = 0; j + k < n && a[i + k] == a[j + k]; ++k);

    if (j + k == n) break;

    (a[i + k] < a[j + k] ? i : j) += k + 1;

    if (i == j) 
        ++j;
    else if (i > j) 
         swap(i, j);
  }
  return i;
}

我的解决方案是对问题Minimum Rotations解决方案的一点修改。

在上面的代码中,每次进入循环时,它都保持i < j,并且所有a[p...n] (0<=p<j && p!=i)都不是最大后缀。然后,为了确定a[i...n]a[j...n]中的哪一个不太像词典,请使用for循环找到制作k的最少a[i+k]!=a[j+k],然后更新i根据{{​​1}} {}和j

我们可以跳过kk的{​​{1}}元素,并且仍然保持所有i不是最大后缀。例如,如果j,那么a[p...n] (0<=p<j && p!=i)不是最大后缀,因为a[i+k]<a[j+k]在词典上大于它。

答案 1 :(得分:0)

想象一下,在一个双人游戏中,两个对手A和B互相攻击,找到给定字符串s的最大后缀。首先找到最大后缀的人将赢得比赛。在第一轮中,A选择后缀s [i ..],B选择后缀s [j ..]。

i: _____X
j: _____Y
Matched length = k

法官比较两个后缀,发现在k比较后存在不匹配,如上图所示。

在不失一般性的情况下,我们假设X> Y,然后B在这一轮中输了。所以他必须选择一个不同的后缀,以便(可能)在下一轮击败A.如果B是聪明的,他将不会从位置j,j + 1,...,j + k开始选择任何后缀,因为s [j ..]已经被s [i ..]击败并且他知道s [ j + 1 ..]将被s [i + 1 ..]击败,s [j + 2 ..]将被s [i + 2 ..]击败,依此类推。所以B应该为下一轮选择后缀S [j + k + 1 ..]。另外一个观察是B不应该选择与A相同的后缀,因为找到最大后缀的第一个人赢了游戏。如果j + k + 1碰巧等于i,则B应跳到下一个位置。

最后,经过多轮比赛后,A或B将会出现选择并输掉比赛,因为A和B的选择数量都是有限的,并且在每轮比赛后都会有一些选择被淘汰。 当发生这种情况时,获胜者持有的当前后缀是最大后缀(记住失败者用尽所有选择。放弃选择,因为它不可能是最大后缀,或者它当前由另一个人持有。所以唯一的原因是失败者在某些回合中放弃了实际的最大后缀是他的对手持有它。一旦玩家持有最大后缀,他将永远不会输掉并放弃它。)

下面的C ++程序几乎就是这个游戏的字面翻译。

int maxSuffix(const std::string& s) {
    std::size_t i = 0, j = 1, k;
    while (i < s.size() && j < s.size()) {
        for (k = 0; i + k < s.size() && j + k < s.size() && s[i + k] == s[j +k]; ++k) { } //judge

        if (j + k >= s.size()) return i; //B is finally lost
        if (i + k >= s.size()) return j; //A is finally lost

        if (s[i + k] > s[j + k]) { //B is lost in this round so he needs a new choice
            j = j + k + 1;
            if (j == i) ++j;
        } else {                   //A is lost in this round so he needs a new choice
            i = i + k + 1;
            if (i == j) ++i;
        }
    }
    return j >= s.size() ? i : j;
}

运行时间分析:最初每个玩家都有n个选择。在每轮之后,裁判进行k次比较,并且从A或B中至少消除了k个可能的选择。因此,当比赛结束时,比较的总数以2n为界。

上面的讨论是在字符串的上下文中,但它应该对任何仅支持顺序访问的容器进行微小的修改。