回文检测效率

时间:2008-10-29 19:46:35

标签: algorithm performance palindrome

我对Jon Limjap's interview mishap感到好奇,并开始寻找有效的方法进行回文检测。我检查了palindrome golf个答案,在我看来答案中只有两个算法,反转字符串并检查尾部和头部。

def palindrome_short(s):
    length = len(s)
    for i in xrange(0,length/2):
        if s[i] != s[(length-1)-i]: return False
    return True

def palindrome_reverse(s):
    return s == s[::-1]

我认为这些方法都不能用于检测巨大DNA序列中的确切回文。我环顾四周,没有找到任何关于这种超高效方式的免费文章。

一种好方法可能是以分而治之的方式并行化第一个版本,为每个线程或处理器分配一对char数组1..n和length-1-n..length-1。

什么是更好的方式?

你知道吗?

9 个答案:

答案 0 :(得分:7)

只有一个回文,你必须在O(N)中做,是的。通过按照您所说的方式拆分字符串,您可以通过多处理器获得更高的效率。

现在说你想做精确的DNA匹配。这些字符串长达数千个字符,并且它们非常重复。这为我们提供了优化的机会。

假设你将一个1000字符长的字符串分成5对100,100。代码如下所示:

isPal(w[0:100],w[-100:]) and isPail(w[101:200], w[-200:-100]) ...

等......第一次进行这些比赛时,你必须处理它们。但是,您可以将已完成的所有结果添加到哈希表映射对中:

isPal = {("ATTAGC", "CGATTA"): True, ("ATTGCA", "CAGTAA"): False}

等......但这会占用过多的记忆。对于100,100对,哈希映射将具有2 * 4 ^ 100个元素。假设你只存储两个32位的字符串哈希作为键,你将需要10 ^ 55兆字节,这是荒谬的。

也许如果你使用较小的字符串,问题可以解决。那么你将有一个巨大的散列图,但至少回文可以说10x10对需要O(1),因此检查1000字符串是否为回文将需要100次查找而不是500次比较。它仍然是O(N),但是......

答案 1 :(得分:3)

第二个功能的另一个变体。我们不需要检查正常和反向字符串的正确部分。

def palindrome_reverse(s):
  l = len(s) / 2
  return s[:l] == s[l::-1]

答案 2 :(得分:2)

除非你进行模糊匹配,否则没有。这可能是他们在DNA中所做的事情(我已经用smith-waterman在DNA中进行了搜索,但这显然要比序列中的回文或反向补码更难。) / p>

答案 3 :(得分:2)

显然,你不可能比O(n)渐近效率更好,因为每个角色必须至少检查一次。但是,你可以获得更好的乘法常数。

对于单个线程,您可以使用程序集获得加速。您也可以通过一次检查大于一个字节的块的数据来做得更好,但由于对齐的考虑,这可能会很棘手。如果可以一次检查大小为16个字节的块,那么使用SIMD会更好。

如果您想对其进行并行化,则可以将字符串划分为N个片段,并让处理器i将片段[i*n/2, (i+1)*N/2)与片段[L-(i+1)*N/2, L-i*N/2)进行比较。

答案 4 :(得分:1)

它们都在O(N)中,因此我认为这些解决方案中没有任何特定的效率问题。也许我没有足够的创造力,但我看不出如何在少于N个步骤中比较N个元素,所以像O(log N)这样的东西绝对不可能恕我直言。

Pararellism可能有所帮助,但它仍然不会改变算法的大哦级别,因为它相当于在更快的机器上运行它。

答案 5 :(得分:0)

从中心进行比较总是更有效率,因为你可以尽早挽救未命中但是它允许你做更快的最大回文搜索,无论你是在寻找最大半径还是所有非重叠的回文。

唯一真正的并行化是你有多个独立的字符串要处理。分裂成大块将会浪费大量的工作来完成每一次失误,并且总是有更多的失误而不是命中。

答案 6 :(得分:-1)

使用Python,短代码可以更快,因为它将负载放入VM的更快内部(并且有整个缓存和其他类似的东西)

def ispalin(x):
   return all(x[a]==x[-a-1] for a in xrange(len(x)>>1))

答案 7 :(得分:-1)

您可以使用哈希表来放置字符,并且每次找到不在表/映射中的元素时,都会有一个计数器变量,其值会增加。如果你检查并找到已经在表中的元素减少计数。

  mutateAndGetPayload: async ({text}, context) => {
      let newTodo = new TodoModel({ text, _creatorUserId: context.user._id });
      await newTodo.save(); 
      return {newTodo}; 
  },
  outputFields: {
    todoEdge: {
      type: GraphQLTodoEdge,
      resolve: async ({newTodo}, args,context) => {
        const todos = await getTodosByUserContext(context.user._id, 'any');
        console.log("cursorForObjectInConnection = ",cursorForObjectInConnection(todos, newTodo)) // this logs null?
        return {
          cursor: cursorForObjectInConnection(todos, newTodo),
          node: newTodo,
        };
      },
    },

答案 8 :(得分:-1)

public class Palindrome{
    private static boolean isPalindrome(String s){
        if(s == null)
            return false;   //unitialized String ? return false
        if(s.isEmpty())     //Empty Strings is a Palindrome 
            return true;
        //we want check characters on opposite sides of the string 
        //and stop in the middle <divide and conquer>
        int left = 0;  //left-most char    
        int right = s.length() - 1; //right-most char

        while(left < right){  //this elegantly handles odd characters string 
            if(s.charAt(left) != s.charAt(right)) //left char must equal 
                return false; //right else its not a palindrome
            left++; //converge left to right 
            right--;//converge right to left 
        }
        return true; // return true if the while doesn't exit 
    }
}

尽管我们正在进行n / 2个计算,但它的O(n) 这也可以使用线程来完成,但是计算会变得混乱,最好避免使用它。这不会测试特殊字符,并且区分大小写。我有执行此操作的代码,但是可以修改此代码以轻松处理它。