【问题标题】:Number of characters matching between two strings in C++C ++中两个字符串之间匹配的字符数
【发布时间】:2014-11-15 16:22:07
【问题描述】:

我正在构建一个用于拼写纠正的小项目,这不是家庭作业。

给定两个字符串 str1 和 str2。必须找出两个字符串之间匹配的字符数。

例如,如果 str1 = "assign" 和 str2 = "assingn",则输出应为 6。

在str2中,字符“a”、“s”、“s”、“i”、“g”、“n”在str1、“assign”中。因此输出应该是 6。

如果 str1 = "sisdirturn" 和 str2 = "disturb",则输出应为 6。

在str2中,字符“d”、“i”、“s”、“t”、“u”、“r”在字符串str1、“sisdirturn”中。因此输出应该是 6。

我尝试了很多次,但我无法得到答案。请帮助解决这个问题,如果有任何改进的想法,请告诉。

这是我目前的尝试:

int char_match (string str1, string str2)
{
    //Take two strings, split them into vector of characters and sort them.
    int i, j, value = 0;
    vector <char> size1, size2;
    char* cstr1 = new char[str1.length() + 1];
    strcpy(cstr1, str1.c_str());
    char* cstr2 = new char[str2.length() + 1];
    strcpy(cstr2, str2.c_str());

    for(i = 0, j = 0 ; i < strlen(cstr1), j < strlen(cstr2); i++, j++)
    {
        size1.push_back( cstr1[i] );
        size2.push_back( cstr2[j] );
    }

    sort (size1.begin(), size1.end() );
    sort (size2.begin(), size2.end() );

    //Start from beginning of two vectors. If characters are matched, pop them and reset the counters.
    i = 0;
    j = 0;

    while ( !size1.empty() )
    {
        out :
        while ( !size2.empty() )
        {

            if (size1[i] == size2[j])
            {
                value++;
                pop_front(size1);
                pop_front(size2);
                i = 0;
                j = 0;
                goto out;
            }
            j++;    
        }
        i++;
    }

    return value;
}

【问题讨论】:

  • 你能解释一下“匹配的字符数”是什么意思吗?特别是在这种情况下,匹配意味着什么?
  • @Veritas 例如,如果 str1 = "assign" 和 str2 = "assingn",则输出应为 6。在 str2 中,字符、"a"、"s"、"s"、" i", "g", "n" 在 str1, "assign" 中。因此输出应为 6。如果 str1 = "sisdirturn" 和 str2 = "disturb",则输出应为 6。在 str2 中,字符、"d"、"i"、"s"、"t"、"u" , "r" 在字符串 str1, "sisdirturn" 中。因此输出应该是 6。
  • 如果你有字符串 "a" 和 "aaa" 应该返回 1 还是 3?

标签: c++ string spell-checking


【解决方案1】:
#include <iostream>
#include <algorithm> // sort, set_intersection

std::string::size_type matching_characters(std::string s1, std::string s2) {
  sort(begin(s1), end(s1));
  sort(begin(s2), end(s2));
  std::string intersection;
  std::set_intersection(begin(s1), end(s1), begin(s2), end(s2),
                        back_inserter(intersection));
  return intersection.size();
}

int main() {
  std::cout << matching_characters("assign", "assingn") << '\n';     // 6
  std::cout << matching_characters("sisdirturn", "disturb") << '\n'; // 6
}

上面使用了排序,所以它有 O(N*log N) 的性能,如果这很重要的话。如果您的所有输入都很小,那么这可能比第二种解决方案更快:

Sora的方案复杂度更好,也可以用标准&lt;algorithm&gt;s简洁实现:

#include <iostream>
#include <algorithm> // for_each
#include <numeric>   // inner_product

int matching_characters(std::string const &s1, std::string const &s2) {
  int s1_char_frequencies[256] = {};
  int s2_char_frequencies[256] = {};
  for_each(begin(s1), end(s1),
           [&](unsigned char c) { ++s1_char_frequencies[c]; });
  for_each(begin(s2), end(s2),
           [&](unsigned char c) { ++s2_char_frequencies[c]; });

  return std::inner_product(std::begin(s1_char_frequencies),
                            std::end(s1_char_frequencies),
                            std::begin(s2_char_frequencies), 0, std::plus<>(),
                            [](auto l, auto r) { return std::min(l, r); });
}

int main() {
  std::cout << matching_characters("assign", "assingn") << '\n';     // 6
  std::cout << matching_characters("sisdirturn", "disturb") << '\n'; // 6
}

为方便起见,我使用了 C++14 功能,例如通用 lambda。如果您的编译器不支持 C++14,您可能需要进行一些修改。


对我来说,使用 sortset_intersection 的解决方案所需的时间大约是这些输入的其他解决方案的 1/4。这是因为对 6 或 7 个元素的数组进行排序和迭代比遍历 256 个元素的数组要快。

sort/set_intersection (3667ns)for_each/inner_product (16,363ns)

一旦输入足够大,速度优势就会反过来。此外,当输入太大而无法利用小字符串优化时,sort/set_intersection 方法将开始进行昂贵的内存分配。

当然,这个性能结果是高度依赖于实现的,所以如果这个例程的性能很重要,你必须自己在你的目标实现上用真实的输入来测试它。如果没关系,那么 O(N) 解决方案是更好的选择。

【讨论】:

  • 你偷了我的答案!顺便说一句,也许使用std::vector 存储intersection(和intersection.reserve(s1.size())会更好,但这并不是那么重要
  • @user2451677 std::string 也有一个 reserve() 方法。我想使用std::string,因为实现支持“小字符串优化”,它可以完全避免小输入的分配。
【解决方案2】:

我不是 100% 了解您实际想要达到的目标,但是如果您想查看单词中有多少字符匹配,这将是一个简单的情况,只需在它们之间运行一个循环,然后每次找到匹配项时加 1,就像这样

int char_match (string str1, string str2)
{
    //Take two strings, split them into vector of characters and sort them.
   unsigned int matches = 0;

   unsigned int stringLength = (str1.length > str2.length) ? str2.length : str1.length;

   for(unsigned int i = 0; i < stringLength; ++i)
   {
       if(str1[i] == str2[i])
       {
           ++matches;
       }
   }

    return matches;
}

但是从您的代码中,您似乎想确切地找出它们有多少相同的字符,也就是说忽略每个字符的实际位置,那么这将是一个相当不同的过程。类似的东西

int char_match (string str1, string str2)
{
    unsigned int str1CharCount[256] = {0};
    unsigned int str2CharCount[256] = {0};

    unsigned int matches = 0;

   for(unsigned int i = 0; i < str1.length; ++i)
   {
       ++str1CharCount[static_cast<unsigned short>(str1[i])];
   }

   for(unsigned int i = 0; i < str2.length; ++i)
   {
       ++str2CharCount[static_cast<unsigned short>(str1[i])];
   }

   for(unsigned int i = 0; i < 256; ++i)
   {
       matches += (str1CharCount[i] > str1CharCount[i]) ? str1CharCount[i] - (str1CharCount[i] - str2CharCount[i]) : str2CharCount[i] - (str2CharCount[i] - str1CharCount[i]);
   }

    return matches;
}

请注意,对于第二个函数,可能有很多更有效的方法,但应该都一样

编辑:

这段代码应该做你想做的事,主要区别在于它检查 ascii 值以确保它是一个有效字符

int char_match (string str1, string str2)
{
    unsigned int str1CharCount[256] = {0};
    unsigned int str2CharCount[256] = {0};

    unsigned int matches = 0;

    for(unsigned int i = 0; i < str1.length; ++i)
    {
        unsigned short aValue = static_cast<unsigned short>(str1[i]);
        if(aValue >= static_cast<unsigned short>('a') && aValue <= static_cast<unsigned short>('z'))
        {
            ++str1CharCount[static_cast<unsigned short>(str1[i]) - 32];
        }
        else if(aValue >= static_cast<unsigned short>('A') && aValue <= static_cast<unsigned short>('Z'))
        {
            ++str1CharCount[static_cast<unsigned short>(str1[i])];
        }
    }

    for(unsigned int i = 0; i < str2.length; ++i)
    {
        ++str2CharCount[static_cast<unsigned short>(str1[i])];
    }

    for(unsigned int i = static_cast<unsigned short>('a'); i <= static_cast<unsigned short>('Z'); ++i)
    {
        matches += (str1CharCount[i] > str1CharCount[i]) ? str1CharCount[i] - (str1CharCount[i] - str2CharCount[i]) : str2CharCount[i] - (str2CharCount[i] - str1CharCount[i]);
    }

    return matches;
}

【讨论】:

  • 感谢您的回答。虽然,我想找出两个字符串之间匹配的字符数。像如果 str1 = "sisdirturn" 和 str2 = "disturb",那么输出应该是 6。可能需要排序,以避免不相关的字符。
  • 要解决这个问题,而不是仅仅对 str1||2charcount 执行 ++,您可以放置​​一个 if 来检查当前 char 的值是否 > 64 且 96 并且 96 和 (str1[i]) - 32" 然后 str2CharCount 也是如此,之后您可以将底部 for 循环设置为从 65 开始并继续直到 i
猜你喜欢
  • 2018-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-25
  • 1970-01-01
  • 1970-01-01
  • 2019-01-12
相关资源
最近更新 更多