单词排名部分完成

时间:2014-06-05 07:43:08

标签: python algorithm haskell

我不确定如何在约束内解决这个问题。

缩短问题的表述:

  1. " Word和#34;任何大写字母序列A-Z(不仅限于"字典词")。
  2. 考虑单词中所有字符的排列列表,按字典顺序排列
  3. 在此类列表中查找原始字词的位置
  4. 不要生成单词的所有可能排列,因为它不适合时间内存约束。
  5. 约束:字长<= 25个字符;内存限制1Gb,任何答案都应该适合64位整数
  6. 原始问题制定:

      

    考虑一个&#34;字&#34;任何大写字母序列A-Z(不仅限于&#34;词典单词&#34;)。对于具有至少两个不同字母的任何单词,还有其他单词由相同的字母组成,但顺序不同(例如,STATIONARILY / ANTIROYALIST,它们都是字典单词;为了我们的目的&#34; AAIILNORSTTY&#34 ;也是一个&#34;字&#34;由与这两个字母相同的字母组成)。然后,我们可以根据每个单词的位置为每个单词分配一个数字,该单词位于由同一组字母组成的所有单词的按字母顺序排序的列表中。一种方法是生成整个单词列表并找到所需单词,但如果单词很长,这将会很慢。编写一个程序,它将一个单词作为命令行参数,并将其编号打印到标准输出。不要使用上面生成整个列表的方法。你的程序应该能够接受25个字母或更少的字母(可能重复一些字母),并且应该使用不超过1 GB的内存并运行不超过500毫秒。我们检查的任何答案都适合64位整数。

    示例单词及其等级:

    ABAB = 2 
    AAAB = 1 
    BAAA = 4 
    QUESTION = 24572 
    BOOKKEEPER = 10743
    

    的示例:

    AAAB - 1
    AABA - 2
    ABAA - 3
    BAAA - 4
    
    AABB - 1
    ABAB - 2
    ABBA - 3
    BAAB - 4
    BABA - 5
    BBAA - 6
    

    我提出了我认为只是部分解决方案。

    想象一下,我有JACBZPUC这个词。我对单词进行排序并获得ABCCJPUZ这应该是单词排名中的排名1。从ABCCJPUZ到以J开头的单词之前的第一个字母单词,我想找到2个单词之间的排列数。

    例如:

    for `JACBZPUC`
    
    sorted --> `ABCCJPUZ`
    
    permutations that start with A -> 8!/2!
    permutations that start with B -> 8!/2!
    permutations that start with C -> 8!/2!
                  Add the 3 values -> 60480
    

    其他C被忽略,因为排列与前一个C具有相同的值(重复)

    此时我的排名从ABCCJPUZ到以J开头的单词之前的单词

    ABCCJPUZ   rank 1       
    ...
    ...         60480 values
    ...
    *HERE*     
    JABCCJPUZ  rank 60481      LOCATION A
    ...
    ...         
    ...
    JACBZPUC   rank ???        LOCATION B
    

    我不确定如何获取地点A和B之间的值:

    以下是我找到60480值的代码

    def perm(word):
        return len(set(itertools.permutations(word)))
    
    def swap(word, i, j):
        word = list(word)
        word[i], word[j] = word[j], word[i]
        print word
        return ''.join(word)
    
    def compute(word):
        if ''.join(sorted(word)) == word:
            return 1
        total = 0
        sortedWord = ''.join(sorted(word))
        beforeFirstCharacterSet = set(sortedWord[:sortedWord.index(word[0])])
        print beforeFirstCharacterSet
        for i in beforeFirstCharacterSet:
            total += perm(swap(sortedWord,0,sortedWord.index(i)))
        return total
    

    这是我在网上找到解决这个问题的解决方案。

      

    考虑n个字母的单词{x1,x2,...,xn}。我的解决方案基于这样的想法,即单词数字将是两个数量的总和:

         
        
    1. 以字母表中低于x1的字母开头的组合数量,以及
    2.   
    3. 我们在以x1开头的安排中走了多远。
    4.         

      诀窍是第二个数量恰好是单词{x2,...,xn}的单词数。这表明了递归实现。

           

      获得第一个数量有点复杂:

           
          
      1. 让uniqLowers = {u1,u2,...,um} =所有低于x1的独特字母
      2.   
      3. 对于每个uj,计算以uj开头的排列数。
      4.   
      5. 添加所有这些。
      6.   

    我想我完成了第1步但不是第2步。我不知道如何完成这部分

    这是Haskell解决方案......我不知道Haskell = /而我正在尝试用Python编写这个程序

    https://github.com/david-crespo/WordNum/blob/master/comb.hs

3 个答案:

答案 0 :(得分:2)

在实际的第一个字母之前找到字母的排列数的想法是好的。但是你的计算:

for `JACBZPUC`

sorted --> `ABCCJPUZ`

permutations that start with A -> 8!/2!
permutations that start with B -> 8!/2!
permutations that start with C -> 8!/2!
              Add the 3 values -> 60480

错了。只有8!/ 2! = 20160年JACBZPUC的排列,因此起始位置不能大于60480.在您的方法中,第一个字母是固定的,您只能置换后面的七个字母。所以:

permutations that start with A:   7! / 2! ==   2520
permutations that start with B:   7! / 2! ==   2520
permutations that start with C:   7! / 1! ==   5040
                                              -----
                                              10080

你不要除以2!找到以C开头的排列,因为七个重写字母是唯一的;那里只剩下一个C.

这是一个Python实现:

def fact(n):
    """factorial of n, n!"""

    f = 1

    while n > 1:
         f *= n
         n -= 1

    return f



def rrank(s):
    """Back-end to rank for 0-based rank of a list permutation"""

    # trivial case
    if len(s) < 2: return 0

    order = s[:]
    order.sort()

    denom = 1

    # account for multiple occurrences of letters
    for i, c in enumerate(order):
        n = 1
        while i + n < len(order) and order[i + n] == c:
            n += 1

        denom *= n

    # starting letters alphabetically before current letter
    pos = order.index(s[0])

    #recurse to list without its head
    return fact(len(s) - 1) * pos / denom + rrank(s[1:])



def rank(s):
    """Determine 1-based rank of string permutation"""

    return rrank(list(s)) + 1



strings = [
    "ABC", "CBA", 
    "ABCD", "BADC", "DCBA", "DCAB", "FRED", 
    "QUESTION", "BOOKKEEPER", "JACBZPUC",
    "AAAB", "AABA", "ABAA", "BAAA"
]

for s in strings:
    print s, rank(s)

答案 1 :(得分:1)

你找到的解决方案的第二部分也是 - 我想 - 我要提出的建议:

要从您所谓的“位置A”转到“位置B”,您必须在其可能的排列中找到单词ACBZPUC的位置。考虑一个 new 问题,你的字恰好比原来的一个位置

答案 2 :(得分:1)

JABCCPUZ(你知道它的位置)和JACBZPUC(你想要找到它的位置)之间的字母表中的单词都以J开头。找到JACBZPUC相对于JABCCPUZ的位置,那么,相当于找到这两个单词的相对位置,删除了初始J,这与你最初尝试解决的问题相同,但是用一个字短一个字。

重复这个过程足够多次,你将留下一个包含单个字符C的单词。已知单个字符的单词的位置始终为1,因此您可以将该单词和所有单词的总和相加先前绝对位置的相对位置。