让lcp
成为生成两个列表之间最长公共前缀数的函数。
例如lcp [1;2;3;1;2] [1;2] = 2
。
对于给定的列表,它有n
个非空后缀,包括其自身。
例如,
[1; 2; 3]的所有后缀是:
[1; 2; 3]
[2; 3]
[3]
此处的问题是在lcp
和list
之间生成所有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
。
我想这里的关键是平等是可转移的,也可以通过部分平等信息引入不平等
答案 0 :(得分:1)
我不认为这可以在O(n)中解决。 但是你可以做出很好的优化。
我会提出一些事情:
在大多数情况下,在处理完前几个元素后,后缀应该变成一个非常短的字符串,然后你不必比较很多字符。 这个想法是,当元素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);
}