求一个字符串中连续出现次数最多的子串

来源:互联网 发布:双色球数据库分析 编辑:程序博客网 时间:2024/06/07 11:45

求一个字符串中连续出现次数最多的子串

这里要注意的是, 字符串必须要连续出现, 不是出现次数最多的最长子串!

首先来分析基于子串的算法。这些算法使用一种直观的思想:枚举可能的子串,然后与原串进行串匹配,在所有解中找到最优的。串匹配有很多算法,包括著名的 Knuth-Morris-Pratt、稍微不那么著名的Boyer-Moore,还有基于自动机的算法。其中KMP以及BM的一个变种的最坏情况时间复 杂度是线性的。利用这两个算法,就可以将整个算法的时间复杂度做到O(n^2),其中n是s的长度。在这个之上,还可以进一步做事情。譬如在枚举这一步, 可以通过一些办法提取出子串的一些特征,减少需要匹配的子串数目,削减匹配的强度。

由于是连续的字符串, 故,可以考虑基于后缀的算法。不难观察到每个子串都是原 串的一个后缀的前缀。如果可以把原串的所有后缀根据它们的前缀组织起来,那么也就可以将问题解决了。有两种成熟的数据结构分别实现了这个思想:后缀数组 (Suffix Array)和后缀树(Suffix Tree)。简单地说,后缀数组即是一个存放了将一个串的所有后缀按字典序排序后的结果的数组,后 缀树则是一个保存了一个串的所有后缀的Trie。从目前已有的构造算法复杂程度的角度来看,后缀数组比后缀数更简单些。因此有如下基于后缀数组的算法:

整个算法分成三部分:1.构造后缀数组;2.计算后缀数组中相邻后缀的最长公共前缀(Longest Common Prefix);3.利用最长公共前缀信息求解问题。下面举一个具体的例子。

设s = "ababa",那么它的所有后缀就是:

ababa baba aba ba a

把它们按照字典序排序之后就是

a aba ababa ba baba

它们原串中出现的位置分别是

4 2 0 3 1

在这个顺序里从上到下相邻的两个后缀的最长公共前缀分别是

a aba (空串) ba

它们的长度和关联的后缀(用在原串中的出现位置表示)分别是

   1    3    0    2
/ \ / \ / \ / \
4    2    0    3    1

在 这个图里面,具有相同前缀的后缀一定是相邻的,而且它们的最长公共前缀的长度决定于两两相邻的最长公共前缀中最短的一个。譬如a、aba、ababa三个 后缀,它们都包含最长公共前缀a。它们在原串出现的位置分别是4、2、0,最长公共前缀的长度1正是上图中1和3的小者。

可见,如果可以 快速地得到上面这些信息,那么就可以得到问题的高效算法。实际上,,构造后缀数组和计算最长公共前缀都有O(n)时间的算法(需要假设串的字母表的大小是 O(n)的,因为算法涉及基数排序)。解决问题的第3步也可以在O(n)时间内完成(同样需要前面的假设)。因此,这个问题是可以在O(n)时间内解决 的。

这里有个简单的, 构造后缀数组的例子(java):
public int[] getSuffixArray(String str)
{
if (str == null)
return null;

// 初始化后缀数组
String[] suffix = new String[str.length()];
for (int i = 0; i < suffix.length; i++)
suffix[i] = str.substring(i);

// 对后缀数组排序
Arrays.sort(suffix);

// 求结果数组
int[] result = new int[str.length()];
for (int i = 0; i < suffix.length; i++)
{
result[i] = str.lastIndexOf(suffix[i]);
}

return result;
}
原创粉丝点击