Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/133.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何切断字符串的某些部分,集合中的每个字符串都有这些部分_C++_String_C++11_C++14_Matching - Fatal编程技术网

C++ 如何切断字符串的某些部分,集合中的每个字符串都有这些部分

C++ 如何切断字符串的某些部分,集合中的每个字符串都有这些部分,c++,string,c++11,c++14,matching,C++,String,C++11,C++14,Matching,我目前的问题如下: 我有一个指向文件的完整路径名的std::vector。 现在我想切断所有字符串的公共前缀 实例 如果向量中有这3个字符串: /home/user/foo.txt /home/user/bar.txt /home/baz.txt 我想切断向量中每个字符串的/home/连接 问题 一般来说,有什么方法可以做到这一点吗? 我想要一个算法,删除所有字符串的公共前缀。 我目前只有一个解决这个问题的方法,就是在m上用n个字符串,m是最长的字符串长度,只需逐个字符地遍历每个字符串。 有没

我目前的问题如下: 我有一个指向文件的完整路径名的std::vector。 现在我想切断所有字符串的公共前缀

实例 如果向量中有这3个字符串:

/home/user/foo.txt
/home/user/bar.txt
/home/baz.txt
我想切断向量中每个字符串的/home/连接

问题 一般来说,有什么方法可以做到这一点吗? 我想要一个算法,删除所有字符串的公共前缀。 我目前只有一个解决这个问题的方法,就是在m上用n个字符串,m是最长的字符串长度,只需逐个字符地遍历每个字符串。
有没有一种更快或更优雅的方法来解决这个问题?

您只需迭代每个字符串。只有利用前缀只能缩短的事实,才能避免不必要地重复字符串的全长:

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

std::string common_prefix(const std::vector<std::string> &ss) {
    if (ss.empty())
        // no prefix
        return "";

    std::string prefix = ss[0];

    for (size_t i = 1; i < ss.size(); i++) {
        size_t c = 0; // index after which the string differ
        for (; c < prefix.length(); c++) {
            if (prefix[c] != ss[i][c]) {
                // strings differ from character c on
                break;
            }
        }

        if (c == 0)
            // no common prefix
            return "";

        // the prefix is only up to character c-1, so resize prefix
        prefix.resize(c);
    }

    return prefix;
}

void strip_common_prefix(std::vector<std::string> &ss) {
    std::string prefix = common_prefix(ss);
    if (prefix.empty())
        // no common prefix, nothing to do
        return;

    // drop the common part, which are always the first prefix.length() characters
    for (std::string &s: ss) {
        s = s.substr(prefix.length());
    }
}

int main()
{
    std::vector<std::string> ss { "/home/user/foo.txt", "/home/user/bar.txt", "/home/baz.txt"};
    strip_common_prefix(ss);
    for (std::string &s: ss)
        std::cout << s << "\n";
}
根据的提示,如果您对输入有更多的先验知识,则可以实现更高效的算法。
特别是,如果您知道输入已排序,那么比较第一个字符串和最后一个字符串就足够了。请参见。

您必须搜索列表中的每个字符串。但是,您不需要比较每个字符串中的所有字符。公共前缀只能变短,因此您只需要与目前为止的公共前缀进行比较。我不认为这会改变big-O的复杂性,但它会对实际速度产生很大的影响


而且,这些看起来像文件名。考虑到许多文件系统倾向于按排序的顺序返回内容,是否对它们进行了排序?如果是这样,你只需要考虑第一个和最后一个元素。如果它们大多是PR,则先考虑第一个和最后一个共同的前缀,然后根据需要进一步迭代所有缩短前缀的其他字符串。

i -找到文件夹深度最小的文件即BZ.TXT——它的根路径是家。 然后遍历其他字符串,看看它们是否以该根开头。
iii-如果是这样,则从所有字符串中删除root。

这完全可以通过std::algorithms完成

简介:

如果尚未排序,请对输入范围进行排序。排序范围中的第一个和最后一个路径 将是最不一样的。最佳情况为ON,最坏情况为ON+N

使用std::misch确定 两条最不相似的道路[无关紧要]

在每个路径上运行,擦除第一个计数字符,其中计数是最长公共序列中的字符数。奥恩

最佳情况时间复杂度:O2N,最坏情况O2N+N.logN有人能检查一下吗

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

std::string common_substring(const std::string& l, const std::string& r)
{
    return std::string(l.begin(),
                       std::mismatch(l.begin(), l.end(),
                                     r.begin(), r.end()).first);
}

std::string mutating_common_substring(std::vector<std::string>& range)
{
    if (range.empty())
        return std::string();
    else
    {
        if (not std::is_sorted(range.begin(), range.end()))
            std::sort(range.begin(), range.end());
        return common_substring(range.front(), range.back());
    }
}

std::vector<std::string> chop(std::vector<std::string> samples)
{
    auto str = mutating_common_substring(samples);
    for (auto& s : samples)
    {
        s.erase(s.begin(), std::next(s.begin(), str.size()));
    }
    return samples;
}

int main()
{
    std::vector<std::string> samples = {
        "/home/user/foo.txt",
        "/home/user/bar.txt",
        "/home/baz.txt"
    };

    samples = chop(std::move(samples));

    for (auto& s : samples)
    {
        std::cout << s << std::endl;
    }   
}
这里有一个备用的“common_substring”,它不需要排序。时间复杂性在理论上是存在的,但在实践中是否更快,您必须检查:

std::string common_substring(const std::vector<std::string>& range)
{
    if (range.empty())
    {
        return {};
    }

    return std::accumulate(std::next(range.begin(), 1), range.end(), range.front(),
                           [](auto const& best, const auto& sample)
                           {
                               return common_substring(best, sample);
                           });
}
更新:

撇开优雅不谈,这可能是最快的方法,因为它避免了任何内存分配,在适当的位置执行所有转换。对于大多数架构和样本大小,这比任何其他性能考虑因素都更重要

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

void reduce_to_common(std::string& best, const std::string& sample)
{
    best.erase(std::mismatch(best.begin(), best.end(),
                             sample.begin(), sample.end()).first,
               best.end());

}

void remove_common_prefix(std::vector<std::string>& range)
{
    if (range.size())
    {
        auto iter = range.begin();
        auto best = *iter;
        for ( ; ++iter != range.end() ; )
        {
            reduce_to_common(best, *iter);
        }

        auto prefix_length = best.size();

        for (auto& s : range)
        {
            s.erase(s.begin(), std::next(s.begin(), prefix_length));
        }
    }
}


int main()
{
    std::vector<std::string> samples = {
        "/home/user/foo.txt",
        "/home/user/bar.txt",
        "/home/baz.txt"
    };

    remove_common_prefix(samples);

    for (auto& s : samples)
    {
        std::cout << s << std::endl;
    }   
}
从std::size\u t index=0;开始;。扫描列表以查看该索引处的字符是否匹配注意:超过结尾的字符不匹配。如果是,则提前索引并重复

完成后,索引将具有前缀长度的值

此时,我建议您编写或查找字符串视图类型。如果这样做,只需为每个字符串str创建一个string_视图,索引的开始/结束为str.size

总成本:O | prefix |*N+N,这也是确认答案正确的成本

如果不想编写字符串视图,只需对向量中的每个str调用str.erasestr.begin、str.begin+索引即可

总成本为O |字符串总长度|+N。必须访问前缀才能确认它,然后必须重写字符串的尾部


现在广度优先的代价是地域性,因为你正在触及各地的记忆。在实践中,可能更有效的方法是分块执行,将前K个字符串扫描到长度Q,找到公共前缀,然后将该公共前缀与下一个块链接起来。这不会改变O表示法,但会改善内存引用的局部性。

这是假设重复部分总是在循环的开始处strings@slawekwin:我想这就是OP所追求的。而不是prefix=prefix.substr0,c;,use prefix.resizec.std::mismatch也可用于替换内部循环。非常感谢您given/home/user/martin,/home/user/mike,您希望结果是artin,ike还是martin,mike?你的标题是字符串,但你的问题是路径名称我很抱歉理解错误。两个结果都可以,如果其中一个可以比另一个更好地实现,我更喜欢这一个我无法想象两个结果都可以实现的用例OK@Exagon你能核对我的答案吗?这就是你想要的吗?@KostasRim不,不是,对不起,这是将军,虽然这不是g
够普遍的了。问题是,如果我有…。他正在寻找一种能自动找到/家/零件的算法。你还应该解释你的答案,而不仅仅是提供代码。我不理解否决票。对我来说,这似乎是最直截了当的答案。OP说集合中的所有字符串共享相同的前缀。首先找到前缀的长度,然后像@Beta那样做,这是很合理的。@MartinNyolt:在我发布后才看到你的评论。所以OP真正要问的问题是首先找到最长的通用前缀,然后去掉它?我认为大多数OS API调用不会按顺序返回文件名。find/-maxdepth 1 | head是我的系统上的一个很好的例子。只有其他用户友好的命令(如unix中的ls)才能排序。所以这取决于文件名的来源。对字符串进行排序将比在未排序的字符串上查找公共前缀慢。这将对字符串进行排序,排序在日志n上,这比OP的算法中的on慢。在这里忽略m,这对两者都是常见的。当然,这可能更优雅,问题是有没有更快或更优雅的方法来解决这个问题@MartinNyolt如果字符串最初被排序,它肯定会更快。排序是回退。回退取决于预期的输入。在最坏的情况下,这会更慢。如果输入通常是随机顺序的,例如文件创建顺序,那么最坏的情况是默认情况,并且您的算法肯定比简单的方法慢。非常感谢您给出了这个非常棒的答案
std::string common_substring(const std::vector<std::string>& range)
{
    if (range.empty())
    {
        return {};
    }

    return std::accumulate(std::next(range.begin(), 1), range.end(), range.front(),
                           [](auto const& best, const auto& sample)
                           {
                               return common_substring(best, sample);
                           });
}
#include <iostream>
#include <vector>
#include <string>

void reduce_to_common(std::string& best, const std::string& sample)
{
    best.erase(std::mismatch(best.begin(), best.end(),
                             sample.begin(), sample.end()).first,
               best.end());

}

void remove_common_prefix(std::vector<std::string>& range)
{
    if (range.size())
    {
        auto iter = range.begin();
        auto best = *iter;
        for ( ; ++iter != range.end() ; )
        {
            reduce_to_common(best, *iter);
        }

        auto prefix_length = best.size();

        for (auto& s : range)
        {
            s.erase(s.begin(), std::next(s.begin(), prefix_length));
        }
    }
}


int main()
{
    std::vector<std::string> samples = {
        "/home/user/foo.txt",
        "/home/user/bar.txt",
        "/home/baz.txt"
    };

    remove_common_prefix(samples);

    for (auto& s : samples)
    {
        std::cout << s << std::endl;
    }   
}