Algorithm 查找字符串中最常见子字符串的算法

Algorithm 查找字符串中最常见子字符串的算法,algorithm,language-agnostic,Algorithm,Language Agnostic,是否有任何算法可以用于查找字符串中最常见的短语(或子字符串)?例如,以下字符串将“hello world”作为其最常见的两个单词短语: “hello world这是hello world。hello world在此字符串中重复三次!” 在上面的字符串中,最常见的字符串(在空字符串字符之后,重复次数无限)是空格字符 有没有办法在这个字符串中生成一个公共子字符串列表,从最常见到最不常见?这是一个类似于Nussinov算法的任务,实际上更简单,因为我们不允许在对齐中出现任何间隙、插入或不匹配 对于长度

是否有任何算法可以用于查找字符串中最常见的短语(或子字符串)?例如,以下字符串将“hello world”作为其最常见的两个单词短语:

“hello world这是hello world。hello world在此字符串中重复三次!”

在上面的字符串中,最常见的字符串(在空字符串字符之后,重复次数无限)是空格字符


有没有办法在这个字符串中生成一个公共子字符串列表,从最常见到最不常见?

这是一个类似于Nussinov算法的任务,实际上更简单,因为我们不允许在对齐中出现任何间隙、插入或不匹配

对于长度为N的字符串A,定义一个
F[-1..N,-1..N]
表格,并使用以下规则填写:

  for i = 0 to N
    for j = 0 to N
      if i != j
        {
          if A[i] == A[j]
            F[i,j] = F [i-1,j-1] + 1;
          else
            F[i,j] = 0;
        }
例如,对于baObaB:

这在
O(n^2)
时间内运行。表中的最大值现在指向最长自匹配子序列的结束位置(i-一次发生的结束,j-另一次)。在开始时,假定数组初始化为零。我添加了条件来排除最长但可能不有趣的自我匹配的对角线

再想一想,这个表在对角线上是对称的,所以只需计算其中的一半就足够了。此外,数组是零初始化的,因此分配零是冗余的。这仍然存在

  for i = 0 to N
    for j = i + 1 to N
      if A[i] == A[j]
         F[i,j] = F [i-1,j-1] + 1;
简短但可能更难理解。计算表包含所有匹配项,短匹配项和长匹配项。您可以根据需要添加进一步的筛选

在下一步中,您需要恢复字符串,从非零单元格按对角线向上和向左。在这一步中,使用一些hashmap来计算同一字符串的自相似匹配数也很简单。对于正常字符串和正常最小长度,只有少量的表单元格将通过此映射进行处理


我认为直接使用hashmap实际上需要O(n^3),因为访问结束时的键字符串必须以某种方式进行比较,以确保相等。这种比较可能是O(n)。

Python。这有点快速和肮脏,数据结构承担了大部分提升工作

from collections import Counter
accumulator = Counter()
text = 'hello world this is hello world.'
for length in range(1,len(text)+1):
    for start in range(len(text) - length):
        accumulator[text[start:start+length]] += 1
计数器结构是一个基于散列的字典,用于计算您看到某个内容的次数。添加到不存在的密钥将创建它,而检索不存在的密钥将得到零而不是错误。因此,您所要做的就是迭代所有子字符串。

Perl,
O(n²)
solution

my$str=“hello world这是hello world。hello world在此字符串中重复三次!”;
我的@words=split(/[^a-z]+/i,$str);
我的($display,$ix,$i,%ocur)=10;
#算计

对于($ix=0;$ix只是伪代码,也许这不是最漂亮的解决方案,但我会这样解决:

function separateWords(String incomingString) returns StringArray{
  //Code
}

function findMax(Map map) returns String{
  //Code
}

function mainAlgorithm(String incomingString) returns String{
    StringArray sArr = separateWords(incomingString);
    Map<String, Integer> map; //init with no content
    for(word: sArr){
        Integer count = map.get(word);
        if(count == null){
            map.put(word,1);
        } else {
            //remove if neccessary
            map.put(word,count++); 
        }
   }
   return findMax(map);
}
函数separateWords(String incomingString)返回StringArray{
//代码
}
函数findMax(映射)返回字符串{
//代码
}
函数main算法(字符串incomingString)返回字符串{
StringArray sArr=分隔符(输入字符串);
Map Map;//不带内容的init
for(字:sArr){
整数计数=map.get(word);
如果(计数=null){
地图放置(单词1);
}否则{
//必要时拆除
map.put(word,count++);
}
}
返回findMax(map);
}

其中map可以包含一个键、值对,就像Java HashMap中的那样。

因为对于长度>=2的字符串的每个子字符串,文本至少包含一个长度为2的子字符串,并且次数相同,所以我们只需要研究长度为2的子字符串

val s = "hello world this is hello world. hello world repeats three times in this string!"

val li = s.sliding (2, 1).toList
// li: List[String] = List(he, el, ll, lo, "o ", " w", wo, or, rl, ld, "d ", " t", th, hi, is, "s ", " i", is, "s ", " h", he, el, ll, lo, "o ", " w", wo, or, rl, ld, d., ". ", " h", he, el, ll, lo, "o ", " w", wo, or, rl, ld, "d ", " r", re, ep, pe, ea, at, ts, "s ", " t", th, hr, re, ee, "e ", " t", ti, im, me, es, "s ", " i", in, "n ", " t", th, hi, is, "s ", " s", st, tr, ri, in, ng, g!)

val uniques = li.toSet
uniques.toList.map (u => li.count (_ == u))
// res18: List[Int] = List(1, 2, 1, 1, 3, 1, 5, 1, 1, 3, 1, 1, 3, 2, 1, 3, 1, 3, 2, 3, 1, 1, 1, 1, 1, 3, 1, 3, 3, 1, 3, 1, 1, 1, 3, 3, 2, 4, 1, 2, 2, 1)

uniques.toList(6)
res19: String = "s "

定义短语的含义,子字符串
“l”
“hello world”
更常见。显然
“hello”
至少和
“hello world”
@amit一样常见。我的意思是“字符串中最常见的子字符串”。然后最常见的子字符串是空字符串(重复无数次).之后的第二个字符是最常见的字符。使用in
O(n)可以很容易地找到它
@amit在找到最常见的字符后,我会尝试找到以每个字符开头的最常见的两个字符串。然后我会尝试找到以最常见的两个字符串开头的最常见的三个字符串,依此类推。“hello world”答案建议您可以使用
作为起始范围(len(text)-length)
并在
的情况下杀死
。True。同时保存一些操作。若要生成列表,请调用:
acculator.most_common()
,可能是因为OP在寻找算法,而不是实现。如果我发布了“我需要一个排序算法,它是O(n log(n)),但在大多数排序数据上不会降级,我宁愿看到“签出”而不是“Java的排序已经针对该用例进行了优化。”--即使它是基于Timsort的。不确定这是否回答了问题。这是一种查找最长自匹配子字符串的简单方法。问题是最常见的自匹配子字符串。我添加了解释,这可以在字符串恢复阶段轻松完成。如果仅字符串高于特定阈值,则该算法是有效的旧的是很有趣的。请注意,您可以通过向下迭代对角线(而不是行或列)来计算矩阵,而无需实际分配任何额外内存,并且可以动态对结果数字进行任何分析,而无需保存它们。这将为您节省O(n^2)内存。