将相邻的相似子串分组

时间:2016-11-19 19:47:17

标签: c#

我正在编写一个程序,我想将相邻的子串分组,例如ABCABCBC可以压缩为2ABC1BC或1ABCA2BC。

在所有可能的选项中,我想找到最小长度的结果字符串。

这是我到目前为止写的代码,但没有做好工作。在这方面请帮助我。

#include <algorithm>
bool findAllDigits(istream & i){
    vector<char> digits(0); char c;

    while(i >> c)//read one char
        if(isdigit(c) && find(digits.begin(), digits.end(), c)==digits.end())//check if it is a number and not in the vector
            digits.push_back(c);

    return digits.size() == 10;//return true if all the digits are in the stream
}

2 个答案:

答案 0 :(得分:2)

您可以使用递归执行此操作。我不能给你一个C#解决方案,因为我这里没有C#编译器,但是一般的想法和python解决方案也可以解决这个问题。

所以你有一个输入字符串ABCABCBC。并且您希望将其转换为run length encoding的高级变体(我们称之为高级RLE )。

我的想法包含一个通用的第一个想法,然后我应用递归:

  1. 总体目标是使用高级RLE 找到字符串的最短表示,让我们创建一个函数shortest_repr(string)
  2. 您可以将字符串分为前缀和后缀,然后检查前缀是否可以在后缀的开头找到。对于您的输入示例,这将是:

    • (A,BCABCBC)
    • (AB,CABCBC)
    • (ABC,ABCBC)
    • (ABCA,BCBC)
    • ...
  3. 此输入可以放入函数shorten_prefix,该函数检查后缀以前缀开头的频率(例如前缀ABC和后缀ABCBC,前缀在后缀的开头只有一次,总共有2 ABC跟随。因此,我们可以将此前缀/后缀组合压缩为输出(2ABC, BC)

    此函数shorten_prefix将在循环中的每个元组上使用。

  4. 使用函数shorten_prefix一次后,大多数字符串组合仍然有后缀。例如。在输出(2ABC, BC)中,仍然有字符串BC作为后缀。因此,需要找到剩余后缀的最短表示。哇,我们仍然有一个名为shortest_repr的函数,所以让我们把它称为剩下的后缀。

  5. 此图像显示此递归的工作原理(我只扩展了第3级之后的一个节点,但实际上所有的橙色圆圈都会经过递归):

    Recursion call stack

    我们从shortest_repr调用字符串ABABB开始在顶部(我选择了较短的图像样本)。然后,我们在所有可能的拆分位置拆分此字符串,并在第二行中获取前缀/后缀对的列表。在此列表的每个元素上,我们首先调用前缀/后缀优化(shorten_prefix)并检索缩短的前缀/后缀组合,该组合已在前缀(第三行)中具有游程长度数字。现在,在每个后缀上,我们调用递归函数shortest_repr

    我没有显示递归的向上方向。当后缀为空字符串时,我们将空字符串传递给shortest_repr。当然,空字符串的最短表示形式是空字符串,因此我们可以立即返回空字符串。 当在我们的循环中收到对shortest_repr的调用结果时,我们只选择循环内的最短字符串并返回它。

    这是一些快速入侵的代码,可以解决这个问题:

    def shorten_beginning(beginning, ending):                                       
        count = 1                                                                   
        while ending.startswith(beginning):                                         
            count += 1                                                              
            ending = ending[len(beginning):]                                        
    
        return str(count) + beginning, ending                                       
    
    
    def find_shortest_repr(string):                                                                                                          
        possible_variants = []                                                      
    
        if not string:                                                              
            return ''                                                               
    
        for i in range(1, len(string) + 1):                                         
            beginning = string[:i]                                                  
            ending = string[i:]                                                     
            shortened, new_ending = shorten_beginning(beginning, ending)            
    
            shortest_ending = find_shortest_repr(new_ending)                        
    
            possible_variants.append(shortened + shortest_ending)                   
    
        return min([(len(x), x) for x in possible_variants])[1]                     
    
    print(find_shortest_repr('ABCABCBC'))                                           
    print(find_shortest_repr('ABCABCABCABCBC'))                                     
    print(find_shortest_repr('ABCABCBCBCBCBCBC')) 
    

    未解决的问题

    我认为这种方法与recursive levenshtein distance calculation存在同样的问题。它多次计算相同的时间。因此,尝试使用动态编程实现此功能将是一个很好的练习。

答案 1 :(得分:0)

如果这不是代码中的学校作业或性能关键部分,RegEx可能就足够了:

string input = "ABCABCBC";
var re = new Regex(@"(.+)\1+|(.+)", RegexOptions.Compiled); // RegexOptions.Compiled is optional if you use it more than once
string output = re.Replace(input, 
    m => (m.Length / m.Result("$1$2").Length) + m.Result("$1$2")); // "2ABC1BC" (case sensitive by default)