删除字符串算法中的重复项
Posted
技术标签:
【中文标题】删除字符串算法中的重复项【英文标题】:Remove duplicates in string algorithm 【发布时间】:2014-06-30 17:54:57 【问题描述】:我的作业是删除随机字符串中的重复项。我的想法是使用 2 个循环来解决问题。
第一个将扫描字符串中的每个字符。 第二个将检查字符是否重复。如果是这样,请删除该字符。
string content = "Blah blah..."
for (int i = 0; i < content.size(); ++i)
char check = content.at(i);
for (int j = i + 1; j < content.size() - 1; ++j)
if (check == content.at(j))
content.erase(content.begin()+j);
问题是它不起作用。它总是删除错误的字符。似乎是索引问题,但我不明白为什么。
临时解决方法是将content.erase(content.begin()+j);
更改为content.erase( remove(content.begin() + i+1, content.end(), check),content.end());
但我认为触发“按值删除”扫描不是一个好方法。我想用 2 个或更少的循环来完成。
任何想法将不胜感激:)
【问题讨论】:
一种简单的方法是使用您拥有的双循环蛮力变体,但将非重复项复制到新字符串中。 可能重复:***.com/questions/2286860/… @JoachimPileborg:我喜欢你的想法,但你能提供一些伪代码吗?我现在无法实现它,不知道 :( @djikay:谢谢你的链接,但你能告诉我我的代码有什么问题吗?我不明白为什么索引是错误的。 @Tiana987642 查看我的回答以了解您的代码有什么问题。 【参考方案1】:您的循环可能如下所示
#include <iostream>
#include <string>
int main()
std::string s = "Blah blah...";
std::cout << '\"' << s << '\"' << std::endl;
for ( std::string::size_type i = 0; i < s.size(); i++ )
std::string::size_type j = i + 1;
while ( j < s.size() )
if ( s[i] == s[j] )
s.erase( j, 1 );
else
++j;
std::cout << '\"' << s << '\"' << std::endl;
return 0;
输出是
"Blah blah..."
"Blah b."
还有许多其他方法使用标准算法。例如
#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
int main()
std::string s = "Blah blah...";
std::cout << '\"' << s << '\"' << std::endl;
auto last = s.end();
for ( auto first = s.begin(); first != last; ++first )
last = std::remove( std::next( first ), last, *first );
s.erase( last, s.end() );
std::cout << '\"' << s << '\"' << std::endl;
return 0;
输出与前面的代码示例相同
"Blah blah..."
"Blah b."
【讨论】:
感谢您的建议 :) 我从您的代码中学到了一些东西 :) 如果您有空闲时间,请告诉我我的代码有什么问题和解决方法,就像您在您的评论:) @Tiana987642 我认为在我的第一个代码示例中将您的循环与我的循环进行比较就足够了。很明显,这个 for (int j = i + 1; j 我的想法是第一个循环取一个字符(我们称之为 A),第二个将 A 与其余的比较并删除。所以第二个将在 A 的位置之后开始,并在字符串的最后一个结束。在我写代码的时候,我没有仔细考虑这一点。只是不想越界。谢谢你指出我的错误:)【参考方案2】:如果可以选择使用 STL,您可以使用 std::unordered_set
来保留目前看到的字符,并使用带有 std::remove_if
的擦除删除习语,如下例所示:
#include <iostream>
#include <string>
#include <unordered_set>
#include <algorithm>
int main()
std::string str("Hello World!");
std::unordered_set<char> log;
std::cout << "Before: " << str << std::endl;
str.erase(std::remove_if(str.begin(), str.end(), [&] (char const c) return !(log.insert(c).second); ), str.end());
std::cout << "After: " << str << std::endl;
LIVE DEMO
【讨论】:
好主意 :) 当然使用 STL 是一种选择,但我想稍微提高一下我的技能,所以我尝试先考虑一个算法,稍后使用<algorithm>
:) 只是我的意见跨度>
@Tiana987642 如果是出于教育目的,我会 100% 与您同在。但是当你真的想做严肃的编程时,不仅强烈推荐 STL,而且恕我直言,它的使用是强制性的。
谢谢,感谢您的建议 :)【参考方案3】:
我建议采用两遍方法。第一遍识别重复字符的位置;第二遍删除它们。
我建议使用std::set
和std::vector<unsigned int>
。向量包含字符串中的字母。该向量包含重复字母的位置。
第一遍检测集合中是否存在字母。如果字母存在,则将位置附加到向量中。否则,该字母被插入到集合中。
对于第二遍,按降序对向量进行排序。 擦除向量中位置的字符,然后从向量中删除该位置。
通过从字符串的末尾向前面擦除字符,当从字符串中擦除字符时,剩余重复项的位置不会改变。
【讨论】:
感谢您的建议:) 如果答案有用,请点击对勾。 我不会忘记谁帮助了我 :) 我已经投票赞成你的答案 :) 我只想公平对待也回答这个问题的人 :D【参考方案4】:我不确定这是导致您的问题的原因,但我在您的代码中看到的另一个问题是在您的第二个 for 循环中。您的 j < content.size() - 1
声明应该只是
j < content.size()
。
起初看这个原因有点棘手,但在这种情况下,您不仅要让向量的大小充当大小,还要充当字符串的结束索引。您正在将最后一个索引缩短一个,这意味着您不会碰到字符串中的最后一个字符。我不知道这是否会帮助您解决最初的问题,但谁知道呢?
【讨论】:
【参考方案5】:注意:您的实际问题是保持对下一个相关元素的正确索引:
如果不删除字符,则下一个元素位于下一个位置。 如果删除一个字符,下一个元素将移动到当前位置的位置(位置保持不变)。另外:还有更有效的解决方案(例如:利用集合)
【讨论】:
现在我明白了。那么你能给我一个解决方法来避免这种情况吗?以上是关于删除字符串算法中的重复项的主要内容,如果未能解决你的问题,请参考以下文章
代码随想录算法训练营第11天 | ● 20. 有效的括号 ● 1047. 删除字符串中的所有相邻重复项 ● 150. 逆波兰表达式求值
LeetCode.1047-重复删除字符串中的所有相邻重复项