省略一组名称

时间:2011-02-14 20:08:29

标签: algorithm ellipsis

好的,我确定有人,某个地方必须已经为此提出了一个算法,所以我想在我开始(重新)发明它之前我会问。

我有一个任意(用户输入的)非空文本字符串列表。每个字符串可以是任意长度(0除外),它们都是唯一的。我想将它们显示给用户,但我想将它们修剪为我决定的固定长度,并用省略号(...)替换它们中的一部分。问题是我希望所有输出字符串都是唯一的。

例如,如果我有字符串:

  • Microsoft Internet Explorer 6
  • Microsoft Internet Explorer 7
  • Microsoft Internet Explorer 8
  • Mozilla Firefox 3
  • Mozilla Firefox 4
  • Google Chrome 14

然后我不想修剪字符串的末尾,因为这是唯一的部分(不想显示“Microsoft Internet ...”3次),但是切出中间部分是可以的:

  • Microsoft ... rer 6
  • Microsoft ... rer 7
  • Microsoft ... rer 8
  • Mozilla Firefox 3
  • Mozilla Firefox 4
  • Google Chrome 14

其他时候,中间部分可能是唯一的,我想修剪结尾:

  • 公司会议纪要,2010年5月25日 - 仅供内部使用
  • 公司会议纪要,2010年6月24日 - 仅供内部使用
  • 公司会议纪要,2010年7月23日 - 仅限内部使用

可能会成为:

  • 公司会议纪要,2010年5月25日......
  • 公司会议纪要,2010年6月24日......
  • 公司会议纪要,2010年7月23日......

我想它应该永远不会对字符串的非常开头进行椭圆化处理,即使这是允许的,因为这看起来很奇怪。而且我猜它可能会在字符串中超过一个位置,但在合理范围内 - 可能2次就可以了,但是3次或更多似乎过多了。或者可能的次数并不像剩下的块的大小那么重要:椭圆之间少于5个字符会毫无意义。

输入(数量和大小)都不会非常大,因此性能不是主要问题(好吧,只要算法不会尝试愚蠢的事情,就像枚举所有可能的字符串一样,直到找到一个集合为止作品!)。

我认为这些要求看起来非常具体,但我实际上相当宽松 - 我只是想描述一下我的想法。

之前有过这样的事吗?是否有一些现有的算法或库可以做到这一点?我用Google搜索了一些但到目前为止没有发现任何类似的内容(但也许我只是在谷歌上搜索)。我不得不相信某个人已经想要解决这个问题了!

2 个答案:

答案 0 :(得分:3)

这听起来像longest common substring problem.

的应用

用省略号替换所有字符串共有的最长子字符串。如果字符串仍然太长而且您可以使用另一个省略号,请重复。

你必须意识到,你可能无法对一组给定的字符串进行“椭圆化”以满足长度要求。

答案 1 :(得分:0)

对字符串进行排序。保留每个字符串的前X个字符。如果此前缀对于字符串之前和之后不是唯一的,则前进直到找到唯一字符(与之前和之后的字符串相比)。 (如果没有找到唯一的字符,则该字符串没有唯一的部分,请参阅帖子的底部)在这些唯一字符之前和之后添加省略号。

请注意,这仍然看起来很有趣:

Microsoft Office -> Micro...ffice
Microsoft Outlook -> Micro...utlook

我不知道您希望使用哪种语言,但这是一个Python实现。

def unique_index(before, current, after, size):
    '''Returns the index of the first part of _current_ of length _size_ that is 
        unique to it, _before_, and _after_. If _current_ has no part unique to it,
        _before_, and _after_, it returns the _size_ letters at the end of _current_'''
    before_unique = False
    after_unique = False
    for i in range(len(current)-size):
        #this will be incorrect in the case mentioned below
        if i > len(before)-1 or before[i] != current[i]:
            before_unique = True
        if i > len(after)-1 or after[i] != current[i]:
            after_unique = True
        if before_unique and after_unique:
            return i

    return len(current)-size

def ellipsize(entries, prefix_size, max_string_length):
    non_prefix_size = max_string_length - prefix_size #-len("...")? Post isn't clear about this.

    #If you want to preserve order then make a copy and make a mapping from the copy to the original
    entries.sort()

    ellipsized = []

    # you could probably remove all this indexing with something out of itertools
    for i in range(len(entries)):
        current = entries[i]

        #entry is already short enough, don't need to truncate
        if len(current) <= max_string_length:
            ellipsized.append(current)
            continue

        #grab empty strings if there's no string before/after
        if i == 0:
            before = ''
        else:
            before = entries[i-1]
        if i == len(entries)-1:
            after = ''
        else:
            after = entries[i+1]

        #Is the prefix unique? If so, we're done.
        current_prefix = entries[i][:prefix_size]    
        if not before.startswith(current_prefix) and not after.startswith(current_prefix):
            ellipsized.append(current[:max_string_length] + '...') #again, possibly -3

        #Otherwise find the unique part after the prefix if it exists.
        else:
            index = prefix_size + unique_index(before[prefix_size:], current[prefix_size:], after[prefix_size:], non_prefix_size)
            if index == prefix_size:
                header = ''
            else:
                header = '...'
            if index + non_prefix_size == len(current):
                trailer = ''
            else:
                trailer = '...'
            ellipsized.append(entries[i][:prefix_size] + header + entries[i][index:index+non_prefix_size] + trailer)
    return ellipsized

另外,你提到字符串本身是唯一的,但它们都有独特的部分吗?例如,“Microsoft”和“Microsoft Internet Explorer 7”是两个不同的字符串,但第一个没有从第二个字符串中唯一的部分。如果是这种情况,那么您将不得不在规范中添加一些内容,以使该案例具有明确性。 (如果将“Xicrosoft”,“MXcrosoft”,“MiXrosoft”等添加到这两个字符串的混合中,则 no 唯一字符串比原始字符串短,以表示“Microsoft”)(考虑它的另一种方式:如果你有所有可能的X字母字符串,你不能将它们全部压缩到X-1或更少的字符串。就像没有压缩方法可以压缩所有输入一样,因为这是本质上是一种压缩方法。)

原帖的结果:

>>> for entry in ellipsize(["Microsoft Internet Explorer 6", "Microsoft Internet Explorer 7", "Microsoft Internet Explorer 8", "Mozilla Firefox 3", "Mozilla Firefox 4", "Google Chrome 14"], 7, 20):
    print entry

Google Chrome 14
Microso...et Explorer 6
Microso...et Explorer 7
Microso...et Explorer 8
Mozilla Firefox 3
Mozilla Firefox 4
>>> for entry in ellipsize(["Minutes of Company Meeting, 5/25/2010 -- Internal use only", "Minutes of Company Meeting, 6/24/2010 -- Internal use only", "Minutes of Company Meeting, 7/23/2010 -- Internal use only"], 15, 40):
    print entry

Minutes of Comp...5/25/2010 -- Internal use...
Minutes of Comp...6/24/2010 -- Internal use...
Minutes of Comp...7/23/2010 -- Internal use...