# 演算法課程題解 - 字串
# TOJ 5
## 題目
https://toj.tfcis.org/oj/pro/5/
## 重點
使用getline(cin,s)以讀取含空白字串
## 解法 By Koios1143
### 程式碼
```cpp=
//By Koios1143
#include<iostream>
using namespace std;
string s;
int main(){
while(getline(cin,s)){
cout<<"Hello ,"<<s<<" !\n";
}
return 0;
}
```
### 複雜度分析
總時間複雜度為 $O(1)$
# TOJ 92
## 題目
https://toj.tfcis.org/oj/pro/92/
## 重點
上課要聽
## 解法 By Koios1143
### 程式碼
```cpp=
//By Koios1143
#include<iostream>
using namespace std;
int main() {
string s1,s2,s3;
cin>>s1>>s2>>s3;
cout<<"Hello, "<<s1<<", "<<s2<<", and "<<s3<<"!\n";
return 0;
}
```
### 複雜度分析
總時間複雜度為 $O(1)$
# TOJ 103
## 題目
https://toj.tfcis.org/oj/pro/103/
輸入兩個字串分別表示茶的種類以及甜度
如果種類和甜度都相同就輸出 GOOD
如果只有一種相同就輸出 =~=
如果兩個都不同就輸出 OTZ
## 解法 By Koios1143
### 想法
先分別記錄名稱和甜度是否相同,再利用條件判斷輸出即可
### 程式碼
```cpp=
//By Koios1143
#include<iostream>
using namespace std;
int main() {
string s1,s2;
int n1,n2;
cin>>s1>>n1>>s2>>n2;
bool name = (s1==s2);
bool sweet = (n1==n2);
if(name && sweet){
cout<<"GOOD\n";
}
else if(name || sweet){
cout<<"=~=\n";
}
else{
cout<<"OTZ\n";
}
return 0;
}
```
### 複雜度分析
總時間複雜度為 $O(1)$
# UVa 272
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?272
給一篇文章,將每個 "" 對分別改寫成 `` 以及 ''
## 解法 By Koios1143
### 程式碼
```cpp=
//By Koios1143
#include<iostream>
using namespace std;
int main(){
string s;
bool first=true;
while(getline(cin,s)){
for(int i=0 ; i<s.size() ; i++){
if(s[i]=='\"'){
if(first)
cout<<"``";
else
cout<<"''";
first=!first;
}
else
cout<<s[i];
}
cout<<"\n";
}
return 0;
}
```
### 複雜度分析
總時間複雜度為 $O(\Sigma len(s))$
# TOJ 100
## 題目
https://toj.tfcis.org/oj/pro/100/
## 重點
[字元的加減運算](https://emanlaicepsa.github.io/2020/10/29/0-5/)
## 解法 By Koios1143
### 程式碼
```cpp=
//By Koios1143
#include<iostream>
using namespace std;
int main() {
char n;
cin>>n;
cout<<(char)(n-1)<<'\n';
return 0;
}
```
### 複雜度分析
總時間複雜度為 $O(1)$
# TOJ 101
## 題目
https://toj.tfcis.org/oj/pro/101/
## 重點
[字元的加減運算](https://emanlaicepsa.github.io/2020/10/29/0-5/)
## 解法 By Koios1143
### 程式碼
```cpp=
//By Koios1143
#include<iostream>
using namespace std;
int main() {
int n;
cin>>n;
cout<<(char)('A'+n-1)<<'\n';
return 0;
}
```
### 複雜度分析
總時間複雜度為 $O(1)$
# TOJ 127
## 題目
https://toj.tfcis.org/oj/pro/127/
凱薩加密
給一個數字 $n$ 以及一個字串 $S$ ,求將字串 $S$ 中的所有字元 $c$ 逆回 $n$ 之後的字串
## 解法 By Koios1143
### 想法
首先考慮到如果 $n$ 大於 26 的情況,其實跟 $n\%26$ 是相同的,所以先處理 $n$
接下來針對每個字元處理位移量
如果超出範圍,可以保證只需要再加上一個 26 就可以得到答案
否則直接扣掉 $n$ 即可
### 程式碼
```cpp=
//By Koios1143
#include<iostream>
using namespace std;
int main(){
int n;
string s;
cin>>n>>s;
n%=26;
for(int i=0 ; i<s.size() ; i++){
if(s[i]-n < 'A')
s[i]=s[i]-n+26;
else
s[i]=s[i]-n;
}
cout<<s<<"\n";
return 0;
}
```
### 複雜度分析
總時間複雜度為 $O(len(s))$
# UVa 458
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?458
有一種特定加密方式
若 K = 2 則 apple 經過加密後會變成 crrng
給你一個字串 請從 Sample Output 及 Sample Input 反推 K
## 解法 By MuMu
### 想法
觀察一下他的加密方式 其實可以滿明顯的觀察出
原文 經過此加密且值為 K 時 密文就會是 原文的 ascii + 7
因此可以從 Input 跟 Output 推出 K
直接 `Input[0] - Output[0]` 就可以推出 K
可得出 K = 7
代表 Output 會等於 Input - 7
[bits/stdc++.h library 介紹](https://www.itread01.com/content/1547093362.html)
### 程式碼
```cpp=
#include<bits/stdc++.h>
using namespace std;
int main(){
string s;
while(cin >> s){
for(int i = 0 ; i < s.size() ; i++){
cout << char(s[i] - 7);
}
cout << endl;
}
}
```
# TOJ 96
## 題目
https://toj.tfcis.org/oj/pro/96/
給一個只有兩個運算元以及一個運算子的式子,求該式子的答案
其中,運算元包含 $+, -, *, /$
## 解法 By Koios1143
### 程式碼
```cpp=
//By Koios1143
#include<iostream>
using namespace std;
int main() {
int a,b;
char c;
cin>>a>>c>>b;
if(c=='+'){
cout<<a<<" + "<<b<<" = "<<a+b<<'\n';
}
else if(c=='-'){
cout<<a<<" - "<<b<<" = "<<a-b<<'\n';
}
else if(c=='*'){
cout<<a<<" * "<<b<<" = "<<a*b<<'\n';
}
else if(c=='/'){
if(b==0)
cout<<"ERROR\n";
else
cout<<a<<" / "<<b<<" = "<<a/b<<" ... "<<a%b<<'\n';
}
return 0;
}
```
### 複雜度分析
總時間複雜度為 $O(1)$
# TOJ 113
## 題目
https://toj.tfcis.org/oj/pro/113/
## 解法 By Koios1143
### 想法
先找出 $P$ 在字串中的位置,再依據移動方向調整即可
### 程式碼
```cpp=
//By Koios1143
#include<iostream>
using namespace std;
int main(){
int n;
string s;
char c;
cin>>n>>s>>c;
int pos=0;
for(int i=0 ; i<n ; i++){
if(s[i]=='P'){
pos=i;
break;
}
}
if(c=='L'){
swap(s[pos],s[pos-1]);
}
else{
swap(s[pos],s[pos+1]);
}
cout<<s<<'\n';
return 0;
}
```
### 複雜度分析
總時間複雜度為 $O(n)$
# TOJ 244
## 題目
https://toj.tfcis.org/oj/pro/244/
將輸入的字串中小寫轉大寫,大寫轉小寫
## 解法 By Koios1143
### 程式碼
```cpp=
//By Koios1143
#include<iostream>
using namespace std;
int main(){
int n;
string s;
cin>>n>>s;
for(int i=0 ; i<s.size() ; i++){
if(s[i]<='Z'){
cout<<(char)(s[i]-'A'+'a');
}
else
cout<<(char)(s[i]-'a'+'A');
}
cout<<"\n";
return 0;
}
```
### 複雜度分析
總時間複雜度為 $O(n)$
# TOJ 18
## 題目
https://toj.tfcis.org/oj/pro/18/
輸入一個字串 $S$
定義一個字串是強效字串,唯有在 $S$ 只留下英文字母時,從頭看到尾與從尾看到頭,不分大小寫是相同的
如果是強效字串,在字串前加上 `SETUP! `
## 解法 By Koios1143
### 想法
先利用一個 tmp 字串記錄字串 $S$ 去除英文字母以外的字元,且將字母都轉為小寫的樣子
接下來只需要檢查 tmp 是不是回文即可
判斷方式可以從同到字串的頭到字串的一半,檢查頭尾是否相同
也就是 $tmp[i] = tmp[tmp.size()-1-i]$ 是否成立
### 程式碼
```cpp=
//By Koios1143
#include<iostream>
using namespace std;
int main(){
string s;
while(getline(cin,s)){
string tmp="";
for(int i=0 ; i<s.size() ; i++){
if((s[i]>='A' && s[i]<='Z') || (s[i]>='a' && s[i]<='z'))
tmp+=tolower(s[i]);
}
bool powerful=true;
for(int i=0, j=tmp.size()-1 ; i<tmp.size()/2 ; i++, j--){
if(tmp[i]!=tmp[j]){
powerful=false;
break;
}
}
if(powerful)
cout<<"SETUP! "<<s<<"\n";
else
cout<<s<<"\n";
}
return 0;
}
```
### 複雜度分析
總複雜度約為 $O(2len(s))$
# TOJ 115
## 題目
https://toj.tfcis.org/oj/pro/115/
## 解法 By Koios1143
### 程式碼
```cpp=
//By Koios1143
#include<iostream>
using namespace std;
string arr[11];
int main(){
for(int i=0 ; i<11 ; i++){
arr[i] = "EMPTY";
}
int n,m;
string s;
cin>>n;
while(n--){
cin>>s>>m;
arr[m]=s;
}
for(int i=1 ; i<=10 ; i++){
cout<<arr[i]<<"\n";
}
return 0;
}
```
### 複雜度分析
總時間複雜度為 $O(nm)$ ,其中 $m$ 表示可以覆蓋的總牌數
# UVa 10082
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?10082
人在打字常常打錯,現在給你一項任務,將所有輸入的字元都變成在鍵盤中左邊一位的字元
當然,題目保證不會輸入像是 **Q** **A** **Z** **\`** 的邊界字元,請放心
## 解法 By Koios1143
### 想法
直接將鍵盤上的字刷過一遍,沒錯,刷過去
建立一個字串 $tbl$ ,包含鍵盤上的所有字母,並且是由上到下、由左到右的順序
接下來每次我們輸入的時候,只需要到 $tbl$ 找到當前的字母,然後輸出他的前一個就可以了
### 程式碼
```cpp=
#include<iostream>
using namespace std;
string tbl="`1234567890-=QWERTYUIOP[]ASDFGHJKL;'ZXCVBNM,./'";
string s;
int main(){
while(getline(cin,s)){
for(int i=0 ; i<s.size() ; i++){
if(s[i]==' ')
cout<<" ";
else{
for(int j=0 ; j<tbl.size() ; j++){
if(s[i]==tbl[j]){
cout<<tbl[j-1];
break;
}
}
}
}
cout<<"\n";
}
return 0;
}
```
### 時間複雜度分析
假設 $tbl$ 的字串長度為 $N$
每次查詢時間複雜度最差為 $O(N)$
每筆測資時間複雜度為 $O(len(s)N)$
## 解法 By MuMu
### 想法
請不要學樓上這樣
乖乖建表
### 程式碼
```cpp=
#include<iostream>
#include<map>
using namespace std ;
int main(){
map <char , char> arr;
string s;
arr['1']='`',arr['2']='1',arr['3']='2',arr['4']='3';
arr['5']='4',arr['6']='5',arr['7']='6',arr['8']='7';
arr['9']='8',arr['0']='9',arr['-']='0',arr['=']='-';
arr['W']='Q',arr['E']='W',arr['R']='E',arr['T']='R';
arr['Y']='T',arr['U']='Y',arr['I']='U',arr['O']='I';
arr['P']='O',arr['[']='P',arr[']']='[',arr['\\']=']';
arr['S']='A',arr['D']='S',arr['F']='D',arr['G']='F';
arr['H']='G',arr['J']='H',arr['K']='J',arr['L']='K';
arr[';']='L',arr['\'']=';',arr['X']='Z',arr['C']='X';
arr['V']='C',arr['B']='V',arr['N']='B',arr['M']='N';
arr[',']='M',arr['.']=',',arr['/']='.',arr[' ']=' ';
while(getline(cin , s)){
for(int i = 0 ; i < s.size() ; i++){
cout << arr[s[i]];
}
cout << "\n";
}
}
```
### 時間複雜度分析
map 實作為紅黑樹
本身查詢複雜度為 $O(logN)$
字串長度為 $S$
整體複雜度 $O(SlogN)$
## 解法 By sa072686
### 想法
請不要學樓上這樣,既不優雅還開了棵又蠢又笨重的紅黑樹
乖乖建表
### 程式碼
```cpp=
#include<iostream>
using namespace std;
string key="`1234567890-=QWERTYUIOP[]\\ASDFGHJKL;'ZXCVBNM,./";
char tbl[128];
int main()
{
int i;
string s;
for (i=1; i<key.size(); i++)
{
tbl[key[i]] = key[i-1];
}
tbl[' '] = ' ';
while (getline(cin, s))
{
for (i=0; i<s.size(); i++)
{
s[i] = tbl[s[i]];
}
cout << s << "\n";
}
}
```
### 時間複雜度分析
計算量為預處理的 $O(1)$ 加上每次詢問時每個字元 $O(1)$ 查表
複雜度為優雅的 $O(N)$
# UVa 11192
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?11192
給一個字串 $S$ 以及一個整數 $N$ ,保證 $len(S)$ 是 $N$ 的整數倍
現在要將字串分成 $N$ 組,並且將每組的字串反過來輸出
## 解法 By Koios1143
### 想法
假設 $M=len(S)/n$
從 $M-1$ 開始,每次把前面 $M$ 個字輸出
再往 $M-1$ 向後移動 $M$,把前面 $M$ 個字輸出
持續這個步驟直到結束
### 程式碼
```cpp=
#include<iostream>
using namespace std;
int n;
string s;
int main(){
while(cin>>n && n){
cin>>s;
n=s.size()/n;
for(int i=n-1 ; i<s.size() ; i+=n){
for(int j=i,cnt=0 ; cnt<n ; j--,cnt++){
cout<<s[j];
}
}
cout<<'\n';
}
return 0;
}
```
### 時間複雜度分析
每筆測資會將字串的每個字都跑過一遍,時間複雜度為 $O(len(S))$
## 另解
使用s.substr()函式以及reverse函式
```cpp=
#include<bits/stdc++.h>
using namespace std;
int group, siz;
string s;
signed main(){
while(cin>>group){
if(group==0) break;
cin>>s;
siz = s.size() / group;
for(int i=0;i<(int)s.size();i+=siz){
string tmp = s.substr(i,siz);
reverse(tmp.begin(),tmp.end());
cout<<tmp;
}
cout<<'\n';
}
}
```
# UVa 11541
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?11541
給一個字串,內容都會是一個字元 $C$ 接著一串數字 $N$ ,表示要輸出 $N$ 個 $C$
例如: `A2B3C1` 表示 `AABBBC`
## 解法 By Koios1143
### 想法
分別用一個變數 $tmp$ 和 $cnt$ 紀錄當前要輸出的字元以及要輸出的次數
每次遇到數字,就把 $cnt*10$ , 再將 $cnt$ 加上當前數字
這樣才可以進位,也因為如此, $cnt$ 的初始值要設定為 0
### 程式碼
```cpp=
#include<iostream>
using namespace std;
int Case=1,t,cnt=0;
string s;
char tmp;
int main(){
cin>>t;
while(t--){
cout<<"Case "<<Case++<<": ";
cin>>s;
cnt=0;
for(int i=0 ; i<=s.size() ; i++){
if(i!=s.size() && s[i]>='0' && s[i]<='9'){
cnt*=10;
cnt+=s[i]-'0';
}
else{
if(cnt==0){
tmp=s[i];
}
else{
for(int j=0 ; j<cnt ; j++){
cout<<tmp;
}
cnt=0;
tmp=s[i];
}
}
}
cout<<"\n";
}
return 0;
}
```
### 時間複雜度分析
假設每次輸出的字元數量是 $N$,需要輸出的字元有 $M$ 個
那麼每筆測資的時間複雜度為 $O(NM)$
總時間複雜度為 $O(tNM)$
# UVa 11577
## 題目
http://domen111.github.io/UVa-Easy-Viewer/?11577
給一個字串,求字串中的所有英文字母轉成小寫後,出現次數最多的有哪些
## 解法 By Koios1143
### 想法
用一個陣列 $cnt$ 儲存每個字母出現的次數,將 $cnt$ 中最大的值對應到的字母輸出
### 程式碼
```cpp=
#include<iostream>
using namespace std;
int cnt[26];
int n,px;
string s;
int main(){
cin>>n;
getchar();
while(n--){
int ans=0;
for(int i=0 ; i<26 ; i++){
cnt[i]=0;
}
getline(cin,s);
for(int i=0 ; i<s.size() ; i++){
if((s[i]>='A' && s[i]<='Z') || (s[i]>='a' && s[i]<='z')){
px=tolower(s[i])-'a';
cnt[px]++;
ans=max(ans, cnt[px]);
}
}
for(int i=0 ; i<26 ; i++){
if(cnt[i]==ans){
cout<<(char)('a'+i);
}
}
cout<<'\n';
}
return 0;
}
```
### 時間複雜度分析
每筆測資會將整個字串看過一遍,時間複雜度為 $O(len(s))$
輸出的時間複雜度很小,可以直接視為 $O(1)$
每筆測資時間複雜度約為 $O(len(s))$
# ZJ a020
## 題目
https://zerojudge.tw/ShowProblem?problemid=a020
給一個身分證字號,求是否符合身份證字號的規則,其規則如下:
1. 先依照表格,將英文字母對應到數字
```
A=10 台北市 J=18 新竹縣 S=26 高雄縣
B=11 台中市 K=19 苗栗縣 T=27 屏東縣
C=12 基隆市 L=20 台中縣 U=28 花蓮縣
D=13 台南市 M=21 南投縣 V=29 台東縣
E=14 高雄市 N=22 彰化縣 W=32 金門縣
F=15 台北縣 O=35 新竹市 X=30 澎湖縣
G=16 宜蘭縣 P=23 雲林縣 Y=31 陽明山
H=17 桃園縣 Q=24 嘉義縣 Z=33 連江縣
I=34 嘉義市 R=25 台南縣
```
2. 將第 1 步得到的數字的**十位數**以及**個位數乘上 9** 的值記錄下來為 $n$
3. 數字從最左邊到最右邊依序乘上 8, 7, 6, ... ,2, 1 ,並相加記錄下來為 $m$
4. 若 $n+m$ 是 10 的倍數,表示正確,否則錯誤
若是正確的身分證輸出 `real` , 否則輸出 `fake`
## 解法 By Koios1143
### 想法
建立一個對照的表格(這裡以 function 實現),讓英文字能對應到相對的數字
接著做第 2 步驟,再判斷即可
建表稍微麻煩,但是只要用心就可以完成了
### 程式碼
```cpp=
#include<iostream>
using namespace std;
string s;
int cnt=0,tmp;
int get_num(char c){
if(c>='A'&&c<='H')
return 10+(c-'A');
else if(c=='I')
return 34;
else if(c>='J' && c<='N')
return 18+(c-'J');
else if(c=='O')
return 35;
else if(c>='P' && c<='V')
return 23+(c-'P');
else if(c=='W')
return 32;
else if(c=='X')
return 30;
else if(c=='Y')
return 31;
else if(c=='Z')
return 33;
}
int main(){
cin>>s;
tmp=get_num(s[0]);
cnt+=(tmp/10)+(tmp%10)*9;
for(int i=1, T=8 ; i<s.size() ; i++,T--){
if(T==0)
cnt+=s[i]-'0';
cnt+=(s[i]-'0')*T;
}
if(cnt%10==0)
cout<<"real\n";
else
cout<<"fake\n";
return 0;
}
```
### 時間複雜度分析
由於字串的長度很小,並且每次計算的量值也很小,直接以 $O(1)$ 記之
## 解法 by sa072686
請不要學樓上,乖乖建表
### 程式碼
```cpp=
#include <iostream>
using namespace std;
// 0123456789012345678901234567890123456789
string id = "__________ABCDEFGHJKLMNPQRSTUVXYWZIO";
int tbl[128];
int main()
{
int i, j, ans;
string s;
for (i=0; i<id.size(); i++)
{
tbl[ id[i] ] = i;
}
while (cin >> s)
{
ans = tbl[s[0]]/10 + (tbl[s[0]]%10 * 9);
for (i=1, j=8; j>0; i++, j--)
{
ans += (s[i]-'0') * j;
}
ans += s[i] - '0';
cout << (ans%10?"fake":"real") << "\n";
}
return 0;
}
```
### 時間複雜度分析
由於字串的長度很小,並且每次計算的量值也很小,直接以 $O(1)$ 記之
###### tags: `SCIST 演算法 題解`