본문 바로가기
백준일지

[C++] STL 자료구조: string

by 민지기il 2024. 3. 28.

string

int main() {
string s(5, 'a'); // s = "aaaaa"
string t = "Hello World!"; // == t("Hello World!")

s.pop_back(); // s = "aaaa"
s.push_back('b'); // s = "aaaab"
s.insert(s.begin() + 1, 'c'); // insert 'c' at s[1], s = "acaaab"
s.insert(s.begin() + 3, 2, 'd'); // insert "dd" at s[3], s = "acaddaab";
s.erase(s.begin() + 1); // erase s[1], s = "aaddaab"
s.erase(s.begin() + 2, s.end()); // erase [s[2], s.end()), s = "aa"
s.insert(s.begin(), t.begin(), t.begin() + 5); // insert [t[0], t[5]) at s[0], s = "Helloaa"

cout << s.size() << '\n'; // 7 ; 요소의 개수


cout<< s.empty() << '\n' << '\n'; // 0 ; 0이 출력됨

s.clear(); // s = "" ; 아무것도 출력 안 됨


s.resize(4); // s = "Hell"
s.resize(3, 'a'); // s = "aaa"

cout << t.substr(0, 5) << '\n'; // "Hello" ; 뽑기

cout<<t.substr(4,6) // t[4]부터 6개
cout << t.substr(6) << '\n'; // "World!"
cout << t.find('!') << '\n'; // 11 (t[11] == '!') ; !의 위치


for (auto it = t.begin(); it != t.end(); it++) cout << *it; cout << '\n'; // "Hello World!"
for (auto it = t.rbegin(); it != t.rend(); it++) cout << *it; cout << '\n'; // "!dlroW olleH"
cout << "t.front() : " << t.front() << '\n'; // 'H'
cout << "t.back() : " << t.back() << '\n'; // '!'
}

이외)

- 대부분의 경우 append보단 +=, + 연산자를 이용하는게 편리하다.

char 하나만을 문자열의 끝에 추가하는 경우엔 push_back이 더 효율적이다.

- find의 시간복잡도는 s.size가 N이고 찾으려 하는 문자열의 길이가 M일 때 O(NM)이다.

따라서 만약 N, M이 100,000정도로 크다면 KMP 등의 문자열 알고리즘을 따로 이용해야 한다.

- stoi, stoll, stod 등을 이용하면 string을 int, long long, double로 변환할 수 있다.

거꾸로 int, long long, double 등에서 string으로 변환하는 것은 to_string 함수를 이용하면 됩니다.

+) isupper, islower, isalpha, isdigit, toupper, tolower

 

주의하기))

1) erase() 범위?

string s;
s = "aaddaab"; 
s.erase(s.begin() + 2, s.end());
cout << s;
//출력: aa
- erase(a,b)에서 [start, end) 범위의 원소를 삭제한다. 
여기서 end는 삭제할 범위의 다음 위치를 가리켜야 한다. 따라서 미만 범위로 그 앞에까지 지워지는 것이다
- s.erase(s.begin() + 2, s.end()-1); 하고 s.end()-1을 하면 end()를 가리키므로 그 앞인 a까지 지워져서 aab만 남는다
결론) [strt,end) 헷갈려하지 말고 그냥 지우기 시작하는 위치와 끝나는 위치를 입력하면 된다.

 

2) string::npos란?

string::nposstring 클래스에서 특정 문자열 또는 문자가 발견되지 않은 경우의 위치를 나타내는 상수이며 size_t 타입으로 정의된다.

int main() {
  string str = "Hello, world!";
  // 문자열 "str"에서 'x'를 찾기
  size_t pos = str.find('x');

  if (pos == string::npos) {
    cout << "문자 'x'를 찾을 수 없습니다." << endl;
  } else {
    cout << "문자 'x'는 " << pos << "번째 위치에 있습니다." << endl;
  }
  return 0;
}

구하는 값이 없는 경우엔 string::npos라는 상수를 반환하는데,

이는 -1을 size_t로 형변환한 값으로 int로 바꾸면 -1과 동일하다.

(s.find(c) != string::npos 대신 s.find(c) != -1 으로 비교해도 -1이 size_t로 암시적 형변환되기 때문에 안전함)

 

<백준>

1152) 단어의 개수 == 문장 입력 시 단어 몇 개인지 세기

int main() {
fastio;
int cnt = 0;
for (string s; cin >> s; cnt++);
cout << cnt << '\n';
}

 

10820) 문장의 소/대/숫자/공백의 수를 각각 세서 띄어쓰기해서 출력하기

int main() {
    fastio;
    string s;
    for (string s; getline(cin, s);) {
        int a = 0, b = 0, c = 0, d = 0;
        for (int i = 0; i < s.size(); i++) {
            if (islower(s[i])) a++;
            else if (isupper(s[i])) b++;
            else if (isdigit(s[i])) c++;
            else d++;
        }
        cout << a << ' ' << b << ' ' << c << ' ' << d << '\n';
    }
}

주의)))) 나는 1문장으로 끝나지 않고 숫자가 출력한 후 새로운 문장을 받아서 또 숫자를 출력하고 싶다 따라서 for loop를 돌려서 문장 입력도 새로 받고 a,b,c,d도 새로 초기화 해줘야 함

 

4458)

int main() {
int N;
cin >> N;
cin.ignore();
for (string s; getline(cin, s);) { //이걸로 계속해서 문장을 입력받는다고 생각하기, 답지에서는 while(N--) 이라고 했다
s[0] = toupper(s[0]);
cout << s<<'\n';
}
}

 

1357)

string을 입력받아 reverse 함수를 써보자 (algorithm 헤더에 있음)

int main(){

    fastio;

    string a,b;

    cin>>a>>b;

    reverse(a.begin(), a.end());

    reverse(b.begin(), b.end());

    string answ=to_string(stoi(a)+stoi(b));

    while(answ.size() && answ.back()=='0') answ.pop_back();

    reverse(answ.begin(),answ.end());

    cout<<answ<<'\n';

}

 

3613)

find 함수 == 문자열 앞에서부터 검색, 검색 문자열이 시작되는 위치를 반환 / 없으면 string::npos 반환

        if(isupper(s[i])){

            s.insert(s[i-1],"_");

            tolower(s[i]);

        } 으로 대문자 앞에 _을 넣으려 했지만 insert는 이렇게 동작하지 않는다.

##insert()

size_t pos = s.find('o');

s.insert(pos, 1, '_'); 처럼 pos라는 반복자를 넣어야 작동한다.

 

bool IsError(const string&s){

    if(s.find('_')==string::npos) return isupper(s[0]); // 오류 case1: _이 없으면(=java이면) 앞에가 대문자인지 (java는 소문자)

    else{ //cpp에서

        if (s[0]=='_' ||s.back()=='_') return 1; // 오류 case2 

        for (auto&i :s) if(isupper(i)) return 1;

        return s.find("_") != string::npos; // _를 찾으면 true(1) 없으면 false(0) 반환

    }

}

 

string JavaToCpp(string s){

    string ret;

    for(int i=0; i<s.size();i++){

        if(isupper(s[i])){

            ret+='_';

            ret+=tolower(s[i]);

        }

        else{ ret+=s[i];}

    }

    return ret;

}

 

string CppToJava(string s){

    //string ret;

    for(int i=0; i<s.size();i++){

        if(s[i]=='_') {

            s.erase(i,1);

            s[i]=toupper(s[i]);}

    }

    return s;

}

int main(){

    fastio;

    string s; cin>>s;

    if(IsError(s)) cout<<"Error!"<<'\n';

    else{

        bool flag = s.find('_')==string::npos; //_못찾으면 -1반환하는데 이는 npos와 일치 ==참

        cout<<(flag?JavaToCpp:CppToJava)(s)<<'\n';

    }

}

오답 1)

CppToJava에서 '_'를 제거하려고 한다. 

for(int i=0; i<s.size(); i++) { s.erase(s[i]); }는 틀리고

for(int i=0; isize(); i++) { s.erase(i,1); }이 맞는 거다

왜냐 erase() 함수는 문자열의 인덱스가 아닌 반복자 사용하기 때문에 위에 코드는 컴파일되지 않는다. 

따라서 문자열 s에서 인덱스 i부터 시작하여 1개의 문자를 제거하는 방식으로 erase 함수를 사용해야 한다.

오답 2)

JavaToCpp에서 ret를 따로 두지 않고 했다.

    for(int i=0; i<s.size(); i++){

        if(isupper(s[i])) {s[i]=tolower(s[i]); s.insert(i,"_");}

    }

'백준일지' 카테고리의 다른 글

[C++] STL 자료구조: Queue  (0) 2024.04.03
[C++] STL 자료구조: stack  (0) 2024.03.31
[C++] STL 자료구조: array, vector  (0) 2024.03.28
[C++]입출력: stringstream  (0) 2024.03.26
[C++] 입출력: cin, cout  (0) 2024.03.23