重複削除を行うC++ の STL 関数 unique の使い方のメモ。
問題はここ:ABC143 C問題
問題の趣旨は
「aabbbbaaca
のような文字列が与えられて、隣合う同じ文字を一つにまとめて abaca
のようにしたら何文字か?」
という感じ。
#コンテスト中に提出したコード
unique のことは知らなかったのでコンテスト中は以下のようなコードを書いた。
submit.cpp
#include <bits/stdc++.h>
using namespace std;
int main(){
int n; cin >> n;
string s; cin >> s;
int ans = 0;
int i = 0;
while(i < n){
char c = s[i];
ans++;
while(s[i] == c){
i++;
if(i >= n) break;
}
}
cout << ans << '\n';
}
見にくいしスマートじゃない…
#unique を使った解法
公式の解答では以下のようなスマートなコードが示されている。
smart.cpp
#include <bits/stdc++.h>
using namespace std;
int main(){
int n; cin >> n;
string s; cin >> s;
s.erase(unique(s.begin(), s.end()), s.end());
cout << s.size() << '\n';
}
確かにスマートだけど unique を知らないから何をしているのかよく分からん…
#unique の使い方
unique は重複削除を行いたい文字列をsとすると
unique(s.begin(), s.end())
とすることで文字列 s を
aabbbcddaaa
⇒ abcda??????
(? はゴミ)
とし、最初の ? を指すポインタを返す。
この返り値を利用してゴミを erase すれば重複削除の完成!
unique.cpp
#include <bits/stdc++.h>
using namespace std;
int main(){
string s = "aabbbcddaaa";
auto n = unique(s.begin(), s.end());
// これで s = abcda となってほしい
cout << s << '\n';//しかし s = abcda?????? とゴミが残る
// unique は返り値として ? の先頭を指すポインタを返す
// そこで n から最後まで erase すれば完成
s.erase(n,s.end());
cout << s << '\n';
}
これを全部まとめてしまえば
smart.cpp
#include <bits/stdc++.h>
using namespace std;
int main(){
int n; cin >> n;
string s; cin >> s;
s.erase(unique(s.begin(), s.end()), s.end());
cout << s.size() << '\n';
}
となるわけですね。
ちなみに unique はもちろん配列にも同じ用法で使える。