如何在 c++ 中找到第一个唯一字符 list<string>?

How to find first unique char a list<string> in c++?

提问人:Sam Carleton 提问时间:7/31/2022 更新时间:7/31/2022 访问量:107

问:

有一个目录的集合(向量、列表等):

示例 1:

/a/ab/bc/de
/a/ab/cc/fw
/a/ab/dd
/a/ab/ee/fg

查找 /a/ab

示例 2:

/a/ab/bc/de
/a/b/cc/fw
/a/ab/dd
/a/ab/ee/fg

查找 /a

找到所有目录的公共路径的最佳方法是什么?

P.S. 最终目标是仅复制相对路径,例如 1 需要删除 /a/ab,以便剩下的就是:

bc/de
cc/fw
dd
ee/fg
C++ 标准 向量

评论

0赞 Dariush Eivazi 7/31/2022
它没有标准算法。您应该根据需要实施它。1.找到最短文本的大小。2. 对于从 0 到 n 的索引,请检查所有字符串是否与第一个字符串具有相同的字符。3. 当达到第一个不相等字符时停止。
0赞 David C. Rankin 7/31/2022
C++11 中可用的基本方法是简单地使用 std::basic_string 并在几个嵌套循环中使用。只需找到第一个(从第二个字符开始),然后遍历剩余的路径以查看初始路径是否匹配,重复 next 并在第一个子字符串匹配失败时中断。通过交替使用 和 获取下一个路径组件,可以避免手动编制索引。findsubstr'/'substr'/'find_first_not_offind_first_of
1赞 David C. Rankin 7/31/2022
“寻求解决方案的问题(”如何......“)必须包括所需的行为、特定的问题或错误以及在问题本身中重现它所需的最短代码。没有明确问题陈述或缺乏重现问题所需的代码的问题对其他人没有用处。请参阅:如何创建最小的可重现示例

答:

0赞 foo 7/31/2022 #1

定义数据集的最佳和大小。 它是一棵树,因此您可以将路径插入树中,然后遍历,直到找到具有多个子节点的节点,此节点是所有节点的公共路径。

1赞 Pepijn Kramer 7/31/2022 #2

这是一种一阶方法,(太糟糕了,我在<filesystem>)

#include <string>
#include <vector>
#include <iostream>

std::string get_common_path(const std::string& lhs, const std::string& rhs)
{
    auto lhs_it = lhs.begin();
    auto rhs_it = rhs.begin();

    // as long as characters match move to right (but not past end of either string)
    while ((lhs_it != lhs.end()) && (rhs_it != rhs.end()) && (*lhs_it == *rhs_it))
    {
        ++lhs_it;
        ++rhs_it;
    }

    return std::string{ lhs.begin(),lhs_it };
}

std::string common_path(const std::vector<std::string>& values)
{
    if (values.empty()) return std::string{};
    if (values.size() == 1) return values.front();

    // get first string, that is now most common path
    auto it = values.begin();
    std::string retval = *it;
    ++it;
    
    // loop over all values
    while ((it != values.end()) && (!retval.empty()))
    {
        // the overlap is the existing overlap combined with the next string
        // in the vector.
        retval = get_common_path(retval, *it);
        ++it;
    }
    
    return retval;
}


int main()
{
    std::vector<std::string> paths
    {
        "/a/ab/bc/de",
        "/a/ab/cc/fw",
        "/a/ab/dd",
        "/a/ab/ee/fg"
    };

    auto result = common_path(paths);
    std::cout << result;
    
    return 0;
}
1赞 John Park 7/31/2022 #3

首先对路径向量进行排序。

std::vector<std::string> paths = {"/a/ab/bc/de", "/a/b/cc/fw", "/a/ab/dd", "/a/ab/ee/fg"};
std::sort(paths.begin(), paths.end());

比较最短路径和最长路径以查找位置不匹配。

const auto& shortest = paths.front();
const auto& longest = paths.back();
auto mis = std::mismatch(shortest.cbegin(), shortest.cend(), longest.cbegin(), longest.cend());

现在从子字符串复制。

auto common = std::string(shortest.cbegin(), mis.first);

以下是在 vs2022 中测试的完整源代码。 它打印了“/a/ab/”和“/a/”作为您的示例。 我相信您可以找到如何删除尾随的“/”。

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

int main() {
  try {
    std::vector<std::string> paths = {"/a/ab/bc/de", "/a/b/cc/fw", "/a/ab/dd",
                                      "/a/ab/ee/fg"};

    std::sort(paths.begin(), paths.end());

    const auto& shortest = paths.front();
    const auto& longest = paths.back();
    auto mis = std::mismatch(shortest.cbegin(), shortest.cend(),
                             longest.cbegin(), longest.cend());

    auto common = std::string(shortest.cbegin(), mis.first);
    std::cout << common << std::endl;
  } catch (const std::exception& e) {
    std::cerr << e.what() << std::endl;
    return -1;
  }

  return 0;
}

评论

0赞 David C. Rankin 7/31/2022
应注意至少需要 c++14。
0赞 A M 7/31/2022 #4

有一个非常简单的解决方案。

您可以分析数据并进行以下观察。

如果将 视为 2 维字符数组,则可以比较字符列。std::vector<std::string>>

/a/ab/bc/de
/a/b/cc/fw      
/a/ab/dd
/a/ab/ee/fg
||||
||||
|||+--- Not all charatcers are the same 
||+---- All characters in this column are the same
|+----- All characters in this column are the same
+------ All characters in this column are the same

从第 0 列开始,您可以检查此列中的所有字符是否相同,然后是下一列,依此类推。

一旦我们发现一列中的差异,那么我们就知道我们已经找到了通用前缀的末尾。

然后我们可以输出通用前缀的结果以及剩余的后缀。

所有这一切都只需要几行常规代码。

一个潜在解决方案的示例:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

std::vector<std::string> paths = { "/a/ab/bc/de", "/a/b/cc/fw", "/a/ab/dd", "/a/ab/ee/fg" };

int main() {
    // Sanity check
    if (not paths.empty()) {

        // Of course we will only compare to the smallest string size
        size_t minSize = std::min_element(paths.begin(), paths.end(), [](const std::string& s1, const std::string& s2) {return s1.size() < s2.size(); })->size();
        size_t cont{ 1 }, col{ 0 };

        // Double nested loop to find resutling column
        for (size_t row{ 1 }; cont and col < minSize; col += cont, row = 1)
            for (auto c{ paths.front()[col] }; cont and row < paths.size(); row += cont)
                cont = ((c == paths[row][col]) * 1);

        // Show result as debug output
        std::cout << "Common prefix: " << paths.front().substr(0, col) << "\n\n";
        for (std::string& s : paths) std::cout << "Resulting path: " << s.substr(col) << '\n';
    }
}