找到具有权重和惩罚的常见近似子串
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”,它会跳过不匹配的字母,这不应该发生。 你说得对,我的代码没用。但是我重写了。以上是关于找到具有权重和惩罚的常见近似子串的主要内容,如果未能解决你的问题,请参考以下文章