我需要从C ++中的一组文件名计算最长的公共子串。
准确地说,我有一个std :: std :: strings的列表(或QT等价,也没关系)
char const *x[] = {"FirstFileWord.xls", "SecondFileBlue.xls", "ThirdFileWhite.xls", "ForthFileGreen.xls"};
std::list<std::string> files(x, x + sizeof(x) / sizeof(*x));
我需要计算所有字符串的n个不同的最长公共子串,在这种情况下,例如对于n = 2
"File" and ".xls"
如果我可以计算最长的公共子序列,我可以将其删除并再次运行算法以获得第二长的,所以基本上归结为:
是否存在用于计算std :: std :: strings列表的LCS的(引用?)实现?
这不是一个好的答案,而是一个肮脏的解决方案 - 在QUrls的QList上强力执行,只从中取出最后一个“/”之后的部分。我很想用“正确的”代码替换它。
(我发现http://www.icir.org/christian/libstree/ - 这会有很大的帮助,但我无法在我的机器上编译它。有人可能会使用它吗?)
QString SubstringMatching::getMatchPattern(QList<QUrl> urls)
{
QString a;
int foundPosition = -1;
int foundLength = -1;
for (int i=urls.first().toString().lastIndexOf("/")+1; i<urls.first().toString().length(); i++)
{
bool hit=true;
int xj;
for (int j=0; j<urls.first().toString().length()-i+1; j++ ) // try to match from position i up to the end of the string :: test character at pos. (i+j)
{
if (!hit) break;
QString firstString = urls.first().toString().right( urls.first().toString().length()-i ).left( j ); // this needs to match all k strings
//qDebug() << "SEARCH " << firstString;
for (int k=1; k<urls.length(); k++) // test all other strings, k = test string number
{
if (!hit) break;
//qDebug() << " IN " << urls.at(k).toString().right(urls.at(k).toString().length() - urls.at(k).toString().lastIndexOf("/")+1);
//qDebug() << " RES " << urls.at(k).toString().indexOf(firstString, urls.at(k).toString().lastIndexOf("/")+1);
if (urls.at(k).toString().indexOf(firstString, urls.at(k).toString().lastIndexOf("/")+1)<0) {
xj = j;
//qDebug() << "HIT LENGTH " << xj-1 << " : " << firstString;
hit = false;
}
}
}
if (hit) xj = urls.first().toString().length()-i+1; // hit up to the end of the string
if ((xj-2)>foundLength) // have longer match than existing, j=1 is match length
{
foundPosition = i; // at the current position
foundLength = xj-1;
//qDebug() << "Found at " << i << " length " << foundLength;
}
}
a = urls.first().toString().right( urls.first().toString().length()-foundPosition ).left( foundLength );
//qDebug() << a;
return a;
}
答案 0 :(得分:1)
如果你说后缀树太重或者不切实际,请注意以下几点 相当简单的蛮力方法可能适合您的应用。
我假设不同的子串应该是非重叠的并从中挑选出来 从左到右。
即使有这些假设,也不需要包含一个独特的集合 “一组字符串中的 N 不同的最长公共子串”。无论 N 是什么, 可能存在多个 N 不同的公共子串,它们都是相同的最大值 长度和其中任何 N 的选择都是任意的。于是 解决方案找到最长的不同共同点的最多 N *集* 所有长度相同的子串都是一组。
算法如下:
Q 是目标长度配额。
字符串是字符串的问题集。
结果是一个最初为空的多图,它将长度映射到一组字符串, 结果[l] 是长度为 l的集合
N ,最初为0,是结果
如果 Q 为0或字符串为空,请返回结果
找到 Strings 的最短成员;保留它的副本 S 并将其删除 来自字符串。我们继续比较 S 的子串 Strings ,因为{ Strings , S }的所有常见子字符串必须是 S 的子串。
迭代生成 S 的所有子串,最长的第一个,使用 由偏移量和长度控制的明显嵌套循环。对于每个子字符串 ss 取值:
如果 ss 不是字符串的常用子字符串,则接下来。
迭代结果[l] l &gt; = ss 的长度直到结束 结果或直到 ss 被发现是被检查的子字符串 结果。在后一种情况下, ss 与已经的结果没有区别 在手,所以接下来。
ss 是常见的子串,与已有的子串不同。迭代 l 的结果[l] &lt; ss 的长度,删除每个结果为a ss 的子字符串,因为所有这些都比 ss 短并且不明显 从中。 ss 现在是一个常见的子字符串,与已有的子字符串不同 剩下的所有其他人都与 ss 不同。
对于 l = ss 的长度,检查 Results [l] 是否存在,即 有任何结果与 ss 的长度相同。如果没有,请拨打电话 NewLength 条件。
同时检查 N == Q ,即我们是否已达到不同的目标配额 长度。如果 NewLength 获得 N == Q ,请调用 StickOrRaise 条件。
如果 StickOrRaise 获得,则将 ss 的长度与 l =进行比较 手中最短结果的长度。如果 ss 短于 l 那么它对我们的配额来说太短了,所以接下来。如果 ss 超过 l 那么所有最短的结果都将被推翻为 ss ,所以删除 结果[l] 并递减 N 。
将 ss 插入结果按其长度键入。
如果 NewLength 获得,则递增 N 。
放弃具有 S 的子串的内部迭代 ss 的偏移量相同但更短,因为它们都不是明显的 来自 ss 。
将外部迭代的 S 中的偏移量增加 ss 的长度, 到下一个非重叠子字符串的开头。
返回结果。
这是一个实现解决方案并用它来演示的程序 字符串列表:
#include <list>
#include <map>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
// Get a non-const iterator to the shortest string in a list
list<string>::iterator shortest_of(list<string> & strings)
{
auto where = strings.end();
size_t min_len = size_t(-1);
for (auto i = strings.begin(); i != strings.end(); ++i) {
if (i->size() < min_len) {
where = i;
min_len = i->size();
}
}
return where;
}
// Say whether a string is a common substring of a list of strings
bool
is_common_substring_of(
string const & candidate, list<string> const & strings)
{
for (string const & s : strings) {
if (s.find(candidate) == string::npos) {
return false;
}
}
return true;
}
/* Get a multimap whose keys are the at-most `quota` greatest
lengths of common substrings of the list of strings `strings`, each key
multi-mapped to the set of common substrings of that length.
*/
multimap<size_t,string>
n_longest_common_substring_sets(list<string> & strings, unsigned quota)
{
size_t nlengths = 0;
multimap<size_t,string> results;
if (quota == 0) {
return results;
}
auto shortest_i = shortest_of(strings);
if (shortest_i == strings.end()) {
return results;
}
string shortest = *shortest_i;
strings.erase(shortest_i);
for ( size_t start = 0; start < shortest.size();) {
size_t skip = 1;
for (size_t len = shortest.size(); len > 0; --len) {
string subs = shortest.substr(start,len);
if (!is_common_substring_of(subs,strings)) {
continue;
}
auto i = results.lower_bound(subs.size());
for ( ;i != results.end() &&
i->second.find(subs) == string::npos; ++i) {}
if (i != results.end()) {
continue;
}
for (i = results.begin();
i != results.end() && i->first < subs.size(); ) {
if (subs.find(i->second) != string::npos) {
i = results.erase(i);
} else {
++i;
}
}
auto hint = results.lower_bound(subs.size());
bool new_len = hint == results.end() || hint->first != subs.size();
if (new_len && nlengths == quota) {
size_t min_len = results.begin()->first;
if (min_len > subs.size()) {
continue;
}
results.erase(min_len);
--nlengths;
}
nlengths += new_len;
results.emplace_hint(hint,subs.size(),subs);
len = 1;
skip = subs.size();
}
start += skip;
}
return results;
}
// Testing ...
int main()
{
list<string> strings{
"OfBitWordFirstFileWordZ.xls",
"SecondZWordBitWordOfFileBlue.xls",
"ThirdFileZBitWordWhiteOfWord.xls",
"WordFourthWordFileBitGreenZOf.xls"};
auto results = n_longest_common_substring_sets(strings,4);
for (auto const & val : results) {
cout << "length: " << val.first
<< ", substring: " << val.second << endl;
}
return 0;
}
输出:
length: 1, substring: Z
length: 2, substring: Of
length: 3, substring: Bit
length: 4, substring: .xls
length: 4, substring: File
length: 4, substring: Word
(使用gcc 4.8.1构建)