【问题标题】:Determine if A is permutation of B using ASCII values使用 ASCII 值确定 A 是否是 B 的排列
【发布时间】:2023-03-21 15:36:01
【问题描述】:

我编写了一个函数来确定字符串a 是否是字符串b 的排列。定义如下:

bool isPermutation(std::string a, std::string b){
    if(a.length() != b.length())
        return false;
    int a_sum, b_sum;
    a_sum = b_sum = 0;
    for(int i = 0; i < a.length(); ++i){
        a_sum += a.at(i);
        b_sum += b.at(i);
    }
    return a_sum == b_sum;
}

我的方法的问题是,如果a = 600000b = 111111,函数返回true。

有什么方法可以保持我对这个问题的一般方法(而不是对字符串进行排序然后执行strcmp)并保持正确性?

【问题讨论】:

  • 如何将字符串中的字符相加?不应该使用 static_cast 或其他东西将它们转换为 int 吗?也许 atoi()?
  • @MikeNickaloff 感谢您的评论。我将字符的 ASCII 值加在一起,而 C++ 不需要我显式地转换它。我希望我的 a = 600000b = 111111 示例不会让您失望,我说的也是它们的 ASCII 值,而不是它们的整数值!
  • @ishyfishy 好吧,我删除了我的答案,因为它在那种情况下并不适用。
  • @MikeNickaloff 问题不在于 OP 在字符串中添加了字符。问题是算法是错误的。计算两个范围内的值的总和绝不会检查一个范围是否是另一个范围的排列。
  • 我想知道如果字符串包含 UTF-8 字符,这个问题的所有答案是否都不会失败。

标签: c++ c++11 permutation


【解决方案1】:

如果您不需要 UTF-8 支持,这是一种简单的方法

这个问题的解决方案非常简单。标准库中有一个函数可以处理这个问题。

假设ab是两个strings:

return is_permutation(a.begin(), a.end(), b.begin(), b.end());

或者,如果您还没有 C++14 的访问权限:

return a.size() == b.size() && is_permutation(a.begin(), a.end(), b.begin());

请注意,虽然它的复杂性只能保证不低于字符串大小的二次方。因此,如果这很重要,那么对两个字符串进行排序确实是一个更好的解决方案:

string aa(a); sort(aa.begin(), aa.end());
string bb(b); sort(bb.begin(), bb.end());
return (aa == bb);

如果这也很慢,请使用上面 John Zwinck 的答案,它的复杂性是线性的。

is_permutation 的文档链接:http://en.cppreference.com/w/cpp/algorithm/is_permutation

sort 的文档链接: http://en.cppreference.com/w/cpp/algorithm/sort

如果需要 UTF-8 支持,则采用一种(稍微)复杂的方法

上述方法在 UTF-8 字符串上可能会失败。这里的问题是 UTF-8 是一种多字节字符编码,也就是说,单个字符可能被编码在多个char 变量中。上面提到的所有方法都没有意识到这一点,并且都假设单个字符也是单个 char 变量。这些方法失败的两个 UTF-8 字符串的示例如下:http://ideone.com/erfNmC

解决方案可能是将我们的 UTF-8 字符串临时复制为固定长度的 UTF-32 编码字符串。假设ab是两个UTF-8编码的strings:

u32string a32 = wstring_convert<codecvt_utf8<char32_t>, char32_t>{}.from_bytes(a);
u32string b32 = wstring_convert<codecvt_utf8<char32_t>, char32_t>{}.from_bytes(b);

那么你就可以在那些 UTF-32 编码的字符串上正确使用上述函数了:

return is_permutation(a32.begin(), a32.end(), b32.begin(), b32.end()) << '\n';

或:

sort(a32.begin(), a32.end());
sort(b32.begin(), b32.end());
return (aa == bb);

缺点是现在 John Zwinck 的方法变得不那么实用了。您必须为 1114112 个元素声明数组,因为这是实际存在的可能 Unicode 字符的数量。

有关转换为 UTF-32 的更多信息:http://en.cppreference.com/w/cpp/locale/wstring_convert/from_bytes

【讨论】:

    【解决方案2】:

    您可以单独计算字符:

    bool isPermutation(std::string a, std::string b)
    {
        if(a.length() != b.length())
            return false;
    
        assert(a.length() <= INT_MAX);
        assert(b.length() <= INT_MAX);
    
        int counts[256] = {};
        for (unsigned char ch : a)
            ++counts[ch];
        for (unsigned char ch : b)
            --counts[ch];
        for (int count : counts)
            if (count)
                return false;
    
        return true;
    }
    

    【讨论】:

    • 感谢您的回答。我只是想澄清counts 的大小是否为 256,因为扩展了 ASCII 假设。另外,为什么将ch 声明为unsigned char
    • @ishyfishy why declare ch as unsigned char? string 持有 char 值,这些值可以是负数。因此,如果您不将它们强制转换为无符号类型,那么理论上您可能会在编写 counts[ch] 时冒着用负值索引数组的风险。
    • @JohnZwinck 为什么不使用string::size_type
    • @gaazkam 我完全不知道 char 值可能是负数!不过现在说得通了。
    • @gaazkam:我会在哪里使用string::size_type?而不是int?如果签名,我发现它更直观。另外,使用int 的缓存行为会更好。如果您希望字符串大小超过 2 GB,请使用 ssize_t
    【解决方案3】:
    std::sort( strOne.begin(), strOne.end() );
    std::sort( strTwo.begin(), strTwo.end() );    
    return strOne == strTwo;
    

    足够了。


    我的建议是使用std::unordered_map

    std::unordered_map< char, unsigned > umapOne;
    std::unordered_map< char, unsigned > umapTwo;
    for( char c : strOne ) ++umapOne[c];
    for( char c : strTwo ) ++umapTwo[c];
    return umapOne == umapTwo;
    

    作为一种优化,您可以在解决方案的顶部添加

    if( strOne.size() != strTwo.size() ) return false;
    

    更好的std::unordered_map解决方案,

    if( strOne.size() != strTwo.size() ) return false; // required
    std::unordered_map< char, int > umap;
    for( char c : strOne ) ++umap[c];
    for( char c : strTwo ) if( --umap[c] < 0 )  return false;
    return true;
    

    如果你只需要解决一个问题而不知道怎么做,你可以使用std::is_permutation

    return std::is_permutation( strOne.begin(), strOne.end(), strTwo.begin(), strTwo.end() );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多