找到具有权重和惩罚的常见近似子串

Posted

技术标签:

【中文标题】找到具有权重和惩罚的常见近似子串【英文标题】:Find common approximate subtring with weight and penalty 【发布时间】:2021-12-27 04:30:32 【问题描述】:

给定两个只有英文字母的随机子串(不一定是相同的长度),使用动态规划找到最常见的近似子串。

每个字母都有一个给定值的权重w(即,将 A 设置为 1,B 设置为 2,或所有字母为 1),并以随机值惩罚 p

最佳“公共近似子串”定义为找到每个字母的最大和为w的最佳连续子串,并在出现不匹配时减去惩罚p

例如,给定两个字符串 "AABCC""AADCC",并带有不匹配惩罚 p=3。当“A”的 w 为 1 且“C”为 2 时,输出将是 "CC",因为它具有最高权重 2 + 2 = 4,超过了所有其他子字符串。如果每个字母的w 是1,那么输出将只是"AA"。等等

现在我可以理解找到最长公共子串https://www.geeksforgeeks.org/print-longest-common-substring/ 的动态方法,但我需要帮助想出一个动态编程算法,以便在 C++ 中找到最佳近似公共子串。我感觉这个问题只是为了找到有重量和惩罚的LCS。

【问题讨论】:

【参考方案1】:

编辑

让我们为权重创建一个类。我不想为所有字母设置权重,因为我设置了default_weight

class LetterWeights

    std::map<char, double> weights;
    const double default_weight;
    const double penalty;
public:
    LetterWeights(const double& def_weight, const double& pen)
        : default_weight(def_weight)
        , penalty(pen)
    ;
    void Set(const char& letter, const double& weight)  weights[letter] = weight; 
    const double Get(const char& letter)
    
        if (weights.find(letter) != weights.end())
        
            return weights[letter] - penalty;
        
        else
        
            return default_weight - penalty;
        
    
;

我认为我们需要自己的子字符串类,我们将有字符串的起始索引和子字符串的长度:

static const int kEmptyIndex = -2;
struct Substring

    int start = kEmptyIndex, len = 0;
    double weight = 0;
    const bool Empty() const return start == kEmptyIndex; 
    void Clear() 
    
        start = kEmptyIndex; 
        len = 0;
        weight = 0;
    
    std::string GetString(const std::string& s)
    
        return s.substr(start, len);
    
;

现在让我们编写 LRC。算法: 输入:string1 (size = N), string2 (size = M)。 结果:一对子字符串及其权重。 假设我们有一个表 X(N x M),如果 string1[i] == string2[j] 则 X(i, j) 等于 1,其他情况下为 0。所以所有子串都在 X 的对角线上:如果 X(0,1), x(1, 2) 和 X(1, 3) = 1 并且其他单元格 = 0 我们可以按长度找到最大子串 string1[0:1 ] 或 string2[1:2] 导致 X(0,1) 和 X(1, 2) 位于一条对角线上。所以我们可以按值填充表格并找到对角线的最大序列。 我们可以填写表格并在O(M*N) 次中找到最大序列。 但我不想创建表。没有它我们可以找到子串,但我们会“通过对角线”找到子串。我们将使用权重函数而不是长度(用于查找最大子字符串); 让我们创建另一个有用的类:

struct MaxSubstring

    Substring max, curr;
    void CheckMax()
    
        if (!curr.Empty() && (max.Empty() || curr.weight > max.weight))
            max = curr;
        curr.Clear();
    
    void Add(const int index, const double& weight)
    
        if (curr.Empty())
        
            curr.start = index;
        
        ++curr.len;
        curr.weight += weight;
    
;

这里我们有关于 diag 的当前和最大子字符串。我需要用于清除复制粘贴的课程。现在我们可以编写主类了:

class LCS

    const std::string string1, string2;
public:
    LCS(const std::string& s1, const std::string& s2)
        : string1(s1)
        , string2(s2)
    ;
    std::pair<std::string, double> FindMax(LetterWeights& weights)
    
        MaxSubstring res;
        
        for (int i = 0; i < string1.size(); ++i)
        
            res.curr = MaxDiagSum(weights, i, 0);
            res.CheckMax();
        
        for (int j = 1; j < string2.size(); ++j)
        
            res.curr = MaxDiagSum(weights, 0, j);
            res.CheckMax();
        
        
        if (res.max.Empty())
        
            return std::make_pair("", 0);
        
        return std::make_pair(res.max.GetString(string1), res.max.weight);
    
    Substring MaxDiagSum(LetterWeights& weights, const int i_start, const int j_start)
    
        MaxSubstring res;
        int i1 = i_start, i2 = j_start;
        for ( ; i1 < string1.size() && i2 < string2.size(); ++i1, ++i2)
        
            if (string1[i1] == string2[i2])
            
                res.Add(i1, weights.Get(string1[i1]));
            
            else 
            
                res.CheckMax();
            
        
        res.CheckMax();
        return res.max;
    
;

主函数举例:

int main(void)

    
    std::string s1, s2;
    std::cin >> s1;
    std::cin >> s2;
    double penalty = 1.0;
    LetterWeights weight(1.0, penalty);
    
    weight.Set('a', 10.0);
    
    LCS lcs(s1, s2);
    auto res = lcs.FindMax(weight);
    std::cout << res.first << " --> " << res.second << std::endl;
    
    return 0;

次:O(M*N);内存:O(max(M, N)).

【讨论】:

你好,我想你有点误解了,返回的子字符串需要是'contiguous',并且所有字母的惩罚值都是一样的。只有每个字母的重量不同。 当我用两个字符串“aadcc”和“aabcc”测试它时,它返回“aacc”,它会跳过不匹配的字母,这不应该发生。 你说得对,我的代码没用。但是我重写了。

以上是关于找到具有权重和惩罚的常见近似子串的主要内容,如果未能解决你的问题,请参考以下文章

PB中取字符串子串的函数是啥

求字符串不同子串个数

数组篇在python中如何查找最长字符串子串

如何在Java中将字符串子串到第二个点(。)?

dp求解各种子串子序列

输入一串字符,找到相同且最长的字符串