如何找到所有兄弟情谊的字符串?

时间:2009-09-24 16:11:49

标签: algorithm complexity-theory

我有一个字符串,另一个包含字符串列表的文本文件。

在按字母顺序排序后,我们将2个字符串称为“兄弟会字符串”。

例如,“abc”和“cba”将被分类为“abc”和“abc”,因此原来的两个是兄弟情谊。但是“abc”和“aaa”不是。

那么,根据提供的一个字符串,有没有一种有效的方法可以从文本文件中挑选出所有兄弟会字符串?

例如,我们有"abc"和一个像这样写的文本文件:

abc
cba
acb
lalala

然后"abc""cba""acb"就是答案。

当然,“排序和比较”是一个不错的尝试,但是通过“高效”,我的意思是如果有一种方法,我们可以确定一个候选字符串是否是原始的一个接一个处理的兄弟情谊。

我认为这是最有效的方式。毕竟,即使没有读取候选字符串,也无法说出答案。对于排序,大多数情况下,我们需要对候选字符串执行多次传递。所以,哈希表可能是一个很好的解决方案,但我不知道选择什么哈希函数。

11 个答案:

答案 0 :(得分:4)

我能想到的最有效的算法:

  • 为原始字符串设置哈希表。让每个字母成为键,字母出现在字符串中的次数就是值。调用此哈希表inputStringTable
  • 解析输入字符串,每次看到一个字符时,都会将哈希条目的值递增一个
  • 表示文件中的每个字符串
  • 创建一个新的哈希表。称之为brotherStringTable。
  • 对于字符串中的每个字符,将一个添加到新的哈希表中。如果brotherStringTable [character]> inputStringTable [character],这个字符串不是兄弟(一个字符显示太多次)
  • 解析字符串后,将每个inputStringTable值与相应的brotherStringTable值进行比较。如果一个不同,那么这个字符串不是兄弟字符串。如果全部匹配,则该字符串是兄弟字符串。

这将是O(n k),其中n是输入字符串的长度(任何超过输入字符串的字符串都可以立即丢弃),k是文件中的字符串数。任何基于排序的算法都是O(n k lg n),所以在某些情况下,这种算法比基于排序的算法更快。

答案 1 :(得分:3)

对每个字符串进行排序,然后进行比较,得出O(N *(k + log S)),其中N是字符串数,k是搜索关键字长度,S是平均字符串长度

似乎计算每个字符的出现可能是一种可能的方式(假设字符串具有合理的长度)。这给你O(k + N * S)。这实际上是否比排序和速度更快比较显然取决于k,N和S的值。

我认为在实践中,与不修改字符串的任何算法相比,在排序情况下重写所有字符串的缓存抖动效果将会扼杀性能......

答案 2 :(得分:2)

迭代,排序,比较。这不应该太难,对吧?

答案 3 :(得分:2)

假设您的字母表从'a'到'z',您可以根据字符索引数组。然后,对于26元素数组中的每个元素,存储该字母在输入字符串中出现的次数。

然后你浏览你正在搜索的字符串集,并遍历每个字符串中的字符。您可以从键字符串中减少与计数数组(的副本)中的每个字母相关联的计数。

如果您完成循环通过候选字符串而不必停止,并且您已经看到与输入字符串中的字符数相同的字符数,那就是匹配。

这允许您跳过排序,支持常量时间数组复制和每个字符串的单次迭代。

编辑:经过进一步反思,这有效地使用bucket sort对第一个字符串的字符进行排序。

答案 4 :(得分:2)

我认为如果两个字符串是字谜,那将对你有所帮助。这是你如何做到的。我假设字符串现在可以包含256个ascii字符。

#define NUM_ALPHABETS 256
int alphabets[NUM_ALPHABETS];

bool isAnagram(char *src, char *dest) {
    len1 = strlen(src);
    len2 = strlen(dest);
    if (len1 != len2)
        return false;

    memset(alphabets, 0, sizeof(alphabets));
    for (i = 0; i < len1; i++)
        alphabets[src[i]]++;
    for (i = 0; i < len2; i++) {
        alphabets[dest[i]]--;
        if (alphabets[dest[i]] < 0)
            return false;
    }

   return true;
}

如果在平均长度'n'的文件中有'm'个字符串,这将在O(mn)中运行

答案 5 :(得分:1)

  • 对查询字符串进行排序
  • 遍历Collection,执行以下操作:
    • 排序当前字符串
    • 与查询字符串比较
    • 如果匹配,这是一个“兄弟会”匹配,保存/索引/无论你想要什么

这就是它。如果你正在进行大量搜索,那么预先分配你的所有收藏将使例行程序更快(以额外的内存为代价)。如果你这样做更多,你可以根据第一个字符等预先排序和保存字典(或一些散列集合),以便更快地找到匹配。

答案 6 :(得分:1)

很明显,每个兄弟会字符串将具有与原始字母相同的字母直方图。构造这样的直方图是微不足道的,并且相当有效地测试输入字符串是否与测试字符串具有相同的直方图(您必须递增或递减计数器两倍于输入字符串的长度)。

步骤如下:

  • 构造测试字符串的直方图(零个数组int histogram[128]和测试字符串中每个字符的增量位置)
  • 每个输入字符串
    • 对于输入字符串c中的每个字符,测试histogram[c]是否为零。如果是,则为不匹配并恢复直方图。

      • 减少histogram[c]
    • 恢复直方图,将输入字符串移回其开始递增而不是递减

对于输入中的每个字符,最多需要两个数组的递增/递减。

答案 7 :(得分:0)

最有效的答案取决于文件的内容。我们提出的任何算法的复杂度都与N(文件中的字数)和L(字符串的平均长度)以及可能的V(字符串长度的变化)成比例

如果这是一个现实世界的情况,我会从KISS开始,而不是试图过度复杂化。检查目标字符串的长度很简单,但可以帮助避免大量的nlogn排序操作。

target = sort_characters("target string")
count = 0
foreach (word in inputfile){
   if target.len == word.len && target == sort_characters(word){
      count++
    }
 }

答案 8 :(得分:0)

我建议:
对于文本文件中的每个字符串:

  1. 将尺寸与“源字符串”进行比较(兄弟情谊字符串的大小应相等)
  2. 比较哈希值(CRC或默认框架哈希应该是好的)
  3. 如果是公平的,请与字符串排序进行更精细的比较。
  4. 这不是最快的算法,但它适用于任何字母/编码。

答案 9 :(得分:0)

这是另一种方法,如果你在字符串中有一组相对较小的可能“字母”,或者对大整数有很好的支持,它就可以工作。基本上包括编写与位置无关的哈希函数......

为每个字母指定不同的素数:

prime['a']=2;
prime['b']=3;
prime['c']=5;

编写一个贯穿字符串的函数,重复将与每个字母关联的素数乘以运行的产品

long long key(char *string)
{
  long long product=1;
  while (*string++) {
    product *= prime[*string];
  }
  return product;
}

此函数将为任何字母集返回一个保证唯一的整数,与它们在字符串中出现的顺序无关。获得“key”的值后,您可以查看要匹配的字符串列表,并执行相同的操作。

当然,时间复杂度为O(N)。您甚至可以通过对密钥进行分解来重新生成(已排序的)搜索字符串。当然,缺点是如果你有一个大的字母表,键会很快变大。

答案 10 :(得分:0)

这是一个实现。它创建了一个主要字母的字典,并且字符串版本与字符串比较相同,将以C ++速度完成。在试用字符串中创建字母的字典时,它会检查主字典,以便在第一个可能的时刻失败 - 如果它找到的字母不在原始字母中,或者更多的字母而不是原始字母,则会失败。如果证明更快,你可以用基于整数的哈希值替换字符串(根据关于基数26的一个答案)。目前,用于比较的哈希看起来像是abacca的a3c2b1。

对于长度为M的N个字符串和长度为K的参考字符串,这应该计算出O(N log(min(M,K))),并且需要尝试字符串的最小查找次数。

master = "abc"
wordset = "def cba accb aepojpaohge abd bac ajghe aegage abc".split()

def dictmaster(str):
    charmap = {}
    for char in str:
        if char not in charmap:
            charmap[char]=1
        else:
            charmap[char] += 1
    return charmap

def dicttrial(str,mastermap):
    trialmap = {}
    for char in str:
        if char in mastermap:
            # check if this means there are more incidences
            # than in the master
            if char not in trialmap:
                trialmap[char]=1
            else:
                trialmap[char] += 1
        else:
            return False
    return trialmap

def dicttostring(hash):
    if hash==False:
        return False
    str = ""
    for char in hash:
        str += char + `hash[char]`
    return str

def testtrial(str,master,mastermap,masterhashstring):
    if len(master) != len(str):
        return False
    trialhashstring=dicttostring(dicttrial(str,mastermap))
    if (trialhashstring==False) or (trialhashstring != masterhashstring):
        return False
    else:
        return True

mastermap = dictmaster(master)
masterhashstring = dicttostring(mastermap)
for word in wordset:
    if testtrial(word,master,mastermap,masterhashstring):
        print word+"\n"