给定一个后缀数组,来自 SRM 630 的 TopCoder 任务要求找到字符串中可以形成具有给定后缀数组的字符串的最小数量的不同字符。完整的问题陈述可以在 TopCoder 网站上找到。
这是ftiasch写的算法:
public int minimalCharacters(int[] array) {
int n = array.length;
int[] position = new int[n + 1];
for (int i = 0; i < n; ++i) {
position[array[i]] = i;
}
position[n] = -1;
int[] minimum = new int[n + 1];
for (int i = n - 1; i >= 0; --i) {
minimum[i] = Integer.MAX_VALUE;
for (int j = i + 1; j <= n; ++j) {
boolean valid = true;
for (int x = i; x < j; ++x) {
for (int y = x + 1; y < j; ++y) {
valid &= position[array[x] + 1] < position[array[y] + 1];
}
}
if (valid && minimum[j] < Integer.MAX_VALUE) {
minimum[i] = Math.min(minimum[i], minimum[j] + 1);
}
}
}
return minimum[0];
}
我知道这是一种动态编程算法,但它是如何工作的?我真的需要一只手来理解它。
编辑
以下是 ftiasch 给我的回信:
嗨,爱丽儿,
首先感谢您的夸奖。坦率地说,我的解决方案并不是解决问题的最佳方案。最佳的运行时间为 O(n),但我的运行时间为 O(n^4)。我只是在比赛中选择了这个想法,因为 n 相对较小。
请记住,相同的字符在 SA 中变得连续。由于问题要求字符数最少,所以我决定使用动态编程将 SA 划分为连续的段,以便每个段以相同的字符开头。
假设 i < j,S[SA[i]] == S[SA[j]] 的必要条件是什么?字典比较表明 suffix(SA[i] + 1) 应该小于 suffix(SA[j] + 1)。我们不难发现,条件也是充分的。
如果您有任何其他问题,请写信给我。:)
编辑1
感谢大卫,我们终于成功了。这是来自 David 的 Python 版本的 java 中的线性时间算法:
public int minimalCharacters(int[] array) {
int n = array.length, i;
if (n == 0)
return 0;
int[] array1 = new int[n + 1];
for (i = 0; i < n; i++)
array1[1 + i] = array[i];
int[] position = new int[n + 1];
for (i = 0; i < n + 1; i++)
position[array1[i]] = i;
int k = 1;
for (i = n; i > 1; i--) {
if (position[array1[i] + 1] <= position[array1[i - 1] + 1])
k++;
}
return k;
}