生成列表及其所有后缀之间的所有最长公共前缀

时间:2014-01-31 13:03:01

标签: algorithm

lcp成为生成两个列表之间最长公共前缀数的函数。

例如lcp [1;2;3;1;2] [1;2] = 2


对于给定的列表,它有n个非空后缀,包括其自身。

例如,

[1; 2; 3]的所有后缀是:

[1; 2; 3]
[2; 3]
[3]


此处的问题是在lcplist之间生成所有all its own suffixes

这里我们讨论列表A和A的所有后缀。没有列表B存在。

例如,对于列表[1;2;3],我想要一个值列表,每个值都是

llcp [1;2;3] [1;2;3]

llcp [1;2;3] [2;3]

llcp [1;2;3] [3]

分别。即,结果应为[3; 0; 0]


使用暴力是直截了当的,但需要O(n^2)

如何在 O(n)

中执行此操作

不要使用后缀树或后缀数组。


修改

我认为绝对可以做 O(n)。我还没有完全弄明白,但我发现了一些有趣的东西。

例如,我们有[x1;x2;x3;x4;x5;x6],那么它的所有后缀都是

row1: [x1;x2;x3;x4;x5;x6]

row2: [x2;x3;x4;x5;x6]

row3: [x3;x4;x5;x6]

row4: [x4;x5;x6]

row5: [x5;x6]

row6: [x6]

我们的目标是计算

lcp row1 row1(显然是n)

lcp row1 row2

lcp row1 row3

lcp row1 row4

lcp row1 row5

lcp row1 row6


所以我们先试试lcp row1 row2

如果我们说lcp row1 row2 = 3,这意味着x1=x2; x2=x3; x3=x4; x4<>x5,对吗?

然后我们可以从上述比较中得到什么?

我们可以知道

  • x1 = x3; x2 = x4; x3 <> x5,对吗?然后我们马上得到lcp row1 row3 = 2,对吧?
  • x1 = x4; x2 <> x5,对吗?然后立即lcp row1 row4 = 1
  • x1 <> x5,然后立即lcp row1 row5 = 0

只留下lcp row1 row6

我想这里的关键是平等是可转移的,也可以通过部分平等信息引入不平等

2 个答案:

答案 0 :(得分:1)

我不认为这可以在O(n)中解决。 但是你可以做出很好的优化。

我会提出一些事情:

  • 首先获取元素1和2的最大后缀
  • 然后获取前一个结果和元素3之间的最大后缀
  • 继续直到你到达终点

在大多数情况下,在处理完前几个元素后,后缀应该变成一个非常短的字符串,然后你不必比较很多字符。 这个想法是,当元素1和2具有n长的后缀时,全局后缀最大可以是n长(但可以更短)。


编辑:

好吧,我想我现在明白了你的问题:D 但我想不出O(n)-way。 这是我最好的尝试(不幸的是在C#中,因为目前我在这台计算机上没有java-IDE ......)

static void Main(string[] args)
{
    Console.WriteLine(string.Join("  ", llcp(new List<char> { 'A', 'B', 'C', 'A', 'A', 'B' }).Select(p => p.ToString()).ToArray()));

    Console.ReadLine();
}

static List<int> llcp(List<char> elements)
{
    List<int> result = new List<int>();
    for (int i = 0; i < elements.Count; i++)
    {
        int j;
        for (j = 0; j < elements.Count; j++)
            if (i+j >= elements.Count || elements[i+j] != elements[j])
                break;
        result.Add(j);
    }
    return result;
}
//Output: 6  0  0  1  2  0

它应该是相当优化的,并且我不确定你是否可以使得显着更快(从理念上讲,可能你可以优化一些对实现的想法)

MfG Mike

答案 1 :(得分:1)

计算列表的z函数(处理就像一个字符串,每个元素作为一个字符)。然后从第i个位置开始的后缀的答案是z(i)。在O(n)中找到z函数算法应该很容易。 这是我的O(n)实现。

   public static List<Integer> getLcp(List<Integer> input) {
            Integer[] lcp = new Integer[input.size()];
            Arrays.fill(lcp, input.size());
            int left = 1;
            int right = -1;
            for (int pos = 1; pos < input.size(); pos++) {
                    if (pos <= right)
                            lcp[pos] = Math.min(lcp[pos - left], right - pos + 1);
                    else
                            lcp[pos] = 0;
                    while (pos + lcp[pos] < input.size() &&
                                    input.get(pos + lcp[pos]) == input.get(lcp[pos]))
                            lcp[pos]++;
                    if (pos + lcp[pos] - 1 > right) {
                            left = pos;
                            right = pos + lcp[pos] - 1;
                    }
            }
            return Arrays.asList(lcp);
    }