隊內訓練第三週
隊內訓練第三週
涉及的知識點
本週練習主要涉及字串
已完成題目 CodeForce:219A、1462B、520A、1367A、978B、855A、1384A、785A、1473B;POJ:1035、1936、2513、3349、2503
未完成題目 CodeForce:1451B、1478B、1476D、1478A;POJ:3080、3274、2151、1840、2002
已完成題目整理
CodeForce 219A
題目大意:給出一個數k,再給出一個字串,判斷該字串能否通過k個相同的字串來組成
思路:判斷每種字元能否整除k即可
程式碼
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
int k;
char str[1212];
int alpha[26],len;
int main()
{
scanf("%d",&k);
getchar();
scanf("%s",str);
len=strlen(str);
for(int i=0; i< len; i++)
alpha[str[i]-'a']++;
for(int i=0; i<26; i++)
if(alpha[i]%k)
{
printf("-1\n");
return 0;
}
for(int t=0; t<k; t++)
for(int i=0; i<26; i++)
for(int j=1; j<=alpha[i]/k; j++)
printf ("%c",'a'+i);
putchar('\n');
return 0;
}
CodeForce 520A
題目大意:判斷給出的字串是否包括所有的26個字母
思路:略
程式碼
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
int n;
bool alphabet[26];
char ch;
int main()
{
scanf("%d",&n);
getchar();
while(n--)
{
ch=getchar();
alphabet[(ch=tolower(ch))-'a']=true;
}
for(int i=0; i<26; i++)
if(!alphabet[i])
{
printf("NO");
return 0;
}
printf("YES");
return 0;
}
CodeForce 1367A
題目大意:給出一個字串,它由多個只用兩個字母的字串合成而來,如abac分解成ab、ba、ac後合成abbaac,求輸入字串的初始形式
思路:直接去掉特定位置字元即可
程式碼
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
int t;
int main()
{
cin >>t;
while(t--)
{
char s[101]= {'\0'};
cin >>s;
int len=strlen(s);
if(len==2)
{
cout <<s<<endl;
continue;
}
for(int i=1; i<len; i+=2)
if(i!=len-1)
s[i]='\0';
for(int i=0; i<len; i++)
if(s[i])
cout <<s[i];
cout <<endl;
}
return 0;
}
CodeForce 978B
題目大意:給出一個字串,不允許出現多於3個x相連,如果出現,判斷刪除多少個字元能滿足條件
思路:直接模擬即可
程式碼
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
int n,op,ans;
char ch;
int main()
{
scanf("%d",&n);
getchar();
while(n--)
{
scanf("%c",&ch);
if(ch=='x')
ans++;
else
ans=0;
if(ans==3)
{
op++;
ans--;
}
}
printf("%d",op);
return 0;
}
CodeForce 855A
題目大意:給出一系列名字,判斷後輸入的是否先前已經出現
思路:直接使用unordered_map
程式碼
#include <iostream>
#include <cstring>
#include <unordered_map>
using namespace std;
unordered_map<string,bool>Names;
int n;
int main()
{
cin >>n;
while(n--)
{
string t;
cin >>t;
if(Names[t])
cout <<"YES";
else
cout <<"NO";
Names[t]=true;
cout <<endl;
}
return 0;
}
CodeForces 1384A
題目大意:給出一個數n,再給出n個數,分別代表編號1與2,2與3…n-1與n間的公共字首的長度,輸出滿足條件的n個字串,結果有很多,只需輸出任意一種
思路:多種結果,那麼假設初始字串均為’a’,根據數字來決定哪一位不同就變哪一位,變完之後直接輸出即可,本題用char陣列會出錯
程式碼
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
int t;
int main()
{
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
string str(200,'a');
cout <<str<<endl;
while(n--)
{
int x;
scanf("%d",&x);
if(str[x]=='a')//如果初始為a則變為b,與原串在這一位不同,反之亦然
str[x]='b';
else
str[x]='a';
//str[x]=str[x]=='a'?'b':'a';
cout <<str<<endl;
}
}
return 0;
}
CodeForce 785A
題目大意:對應字串有對應值,求和
思路:略
程式碼
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
int ans[26]={0,0,6,12,0,0,0,0,20,0,0,0,0,0,8,0,0,0,0,4,0,0,0,0,0,0},N,sum;
int main()
{
scanf("%d",&N);
getchar();
while(N--)
{
char tmp[30];
scanf("%s",tmp);
getchar();
sum+=ans[tmp[0]-'A'];
}
printf("%d",sum);
return 0;
}
CodeForce 1473B
題目大意:定義最小公倍字串,對於兩個字串,如果存在一個字串可以由這兩串複製自己構成,且該字串在所有符合條件的串中長度最短,則這樣的字串為最小公倍字串,現輸入多組字串,求其最小公倍字串
思路:最小公倍字串的長度必定小於等於兩長度之積,因為如果最小公倍字串存在,它的長度=A串重複單元個數×B串重複單元個數/個數的最大公因數,而重複單元個數一定小於等於本身長度,除以最大公因數後更是如此。又最小公倍字元一定是AB串中較小串長度的倍數,所以直接倍增較小串,判斷在長度之積前倍增後的較小串能否“整除”較長串
程式碼
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
using namespace std;
int Q,len,lena,lenb;
string a,b,tmp;
bool flag;
bool Judge()
{
if(len%lenb!=0) return false;
for(int i=0; i<len; i++)
if(tmp[i]!=b[i%lenb])
{
//cout <<tmp[i]<<" "<<b[i%lenb]<<endl;
return false;
}
return true;
}
int main()
{
scanf("%d",&Q);
while(Q--)
{
tmp="";
flag=false;
len=0;
cin >>a>>b;
if(a.size()>b.size())
swap(a,b);
lena=a.length(),lenb=b.length();
while(len<=lena*lenb)
{
tmp+=a;
//cout <<tmp<<endl;
len=tmp.length();
if((flag=Judge())==true)
break;
}
if(flag)
cout <<tmp<<endl;
else
cout <<"-1"<<endl;
}
return 0;
}
POJ 1035
題目大意:先輸入多個字串作為詞典,再輸入多個字串作為查詢的單詞,如果單詞屬於字典,則輸出正確資訊,如果不屬於,則判斷修改一位或刪除一位或插入一位是否能變成屬於字典的單詞,按照能構成單詞的順序輸出
思路:模擬該過程即可,暴力列舉,為了確保輸出順序,將字典中的每一個單詞與輸入的單詞進行長度比較,如果前者等於後者,嘗試修改,剛好多1,嘗試插入,剛好少1,嘗試刪除
程式碼
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef struct letter
{
int len;
char s[17];
} let;
let str[10017];
void Repl(char *s, char *ss)
{
int len = strlen(s),flag = 0,i = 0, j = 0;
while(i < len)
{
if(flag >= 2)
break;
if(s[i++] != ss[j++])
flag++;
}
if(flag < 2)
printf(" %s",s);
}
void Inse(char *s, char *ss)
{
int len = strlen(ss),flag = 0,i = 0, j = 0;
while(j < len)
{
if(flag >= 2)
break;
if(s[i] == ss[j++])
i++;
else
flag++;
}
if(flag < 2)
printf(" %s",s);
}
void Dele(char *s, char *ss)
{
int len = strlen(s),flag = 0,i = 0, j = 0;
while(i < len)
{
if(flag >= 2)
break;
if(s[i++] == ss[j])
j++;
else
flag++;
}
if(flag < 2)
printf(" %s",s);
}
int main()
{
char ss[17];
int k = 0;
while(scanf("%s",str[k].s)!=EOF)
{
str[k].len = strlen(str[k].s);
if(str[k].s[0]=='#')
break;
k++;
}
while(scanf("%s",ss)!=EOF)
{
if(ss[0] == '#')
break;
int len = strlen(ss),flag = 0;
for(int i = 0; i < k; i++)
{
if(!strcmp(ss,str[i].s))
{
printf("%s is correct\n",ss);
flag = 1;
break;
}
}
if(flag)
continue;
printf("%s:",ss);
for(int i = 0; i < k; i++)
{
if(len == str[i].len)
Repl(str[i].s,ss);
else if(len == str[i].len+1)
Inse(str[i].s,ss);
else if(len == str[i].len-1)
Dele(str[i].s,ss);
}
printf("\n");
}
return 0;
}
POJ 1936
題目大意:給出兩個字串,判斷前者是否為後者縮寫
思路:以第一個字串為基準找到第二個字串與其對應元素相等的位置,在此基礎上繼續延伸
程式碼
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
string s,t;
int main()
{
while(cin >>s>>t)
{
int lens=s.length(),lent=t.length(),pos=-1;
bool flag=false;
for(int i=0; i<lens; i++)
{
for(int j=pos+1; j<lent; j++)
{
if(s[i]==t[j])
{
flag=true;
pos=j;
break;
}
}
if(flag&&i!=lens-1)
{
flag=false;
continue;
}
else
break;
}
if(flag)
cout <<"Yes";
else
cout <<"No";
cout <<endl;
}
return 0;
}
POJ 2513
題目大意:給出多根木棒,頭和尾各有自己的顏色,只有顏色相同一端才能相連,且每次只能兩根木棒相連,現在判斷所有木棒能否連成一條直線
思路:判斷歐拉回路(通過圖中每條邊且只通過一次,並且經過每一頂點),將輸入的各顏色看成圖上的節點,將字串使用字典樹雜湊獲得對應的編號,利用編號+並查集來判斷圖是否連通,再記錄每個點的度,判斷度為奇數的點是否為0個或3個
程式碼
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
int degree[500001],pre[500001],ans;
struct Trie
{
bool flag;
int id;
Trie*next[30];
Trie()
{
flag=false;
for(int i=0; i<30; i++)
next[i]=0;
}
} root;
int Hash(char*str)
{
int len=strlen(str);
Trie*t=&root;
for(int i=0; i<len; i++)
{
int id=str[i]-'a';
if(!t->next[id])
t->next[id]=new Trie();
t=t->next[id];
}
if(!t->flag)
{
t->flag=1;
t->id=ans++;
}
return t->id;
}
int Seek(int x)
{
if(pre[x]==x)return x;
return pre[x]=Seek(pre[x]);
}
void Union(int x,int y)
{
int fx=Seek(x),fy=Seek(y);
if(fx!=fy)
pre[fx]=fy;
}
int main()
{
char s[20],t[20];
for(int i=1; i<=500000; i++)
pre[i]=i;
while(~scanf("%s%s",s,t))
{
int x=Hash(s),y=Hash(t);
Union(x,y);
degree[x]++;
degree[y]++;
}
ans--;
int cnt=0,odd=0;
for(int i=1; i<=ans; i++)
{
if(degree[i]%2==1)
odd++;
if(pre[i]==i)
cnt++;
if(cnt>=2||odd>=3)
{
cout<<"Impossible"<<endl;
return 0;
}
}
cout<<"Possible"<<endl;
return 0;
}
POJ 3349
題目大意:給出多個雪花的6個角對應的角的個數,判斷這些雪花中是否有順時針看來相同或者逆時針看來相同的
思路:使用雜湊的方法(求餘)求取值,採用鏈地址法解決衝突,採用vector會超時,判斷順時針和逆時針相同分別判斷A[i]==B[(i+j)%6](i為第i片雪花,j為B順時針的偏移量)和A[i]==B[(5-i-j)%6]
程式碼
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
using namespace std;
int Prime = 999983,n,key;
int sum=0;
typedef struct node
{
int data[6],next;
} node;
node e[100100];
node x;
int hash[999984];//雜湊只儲存相等值的遍歷到的最後一個元素,每個雪花儲存自己的下一個相同值雪花
bool flag = false,judge;
int main()
{
scanf("%d",&n);
memset(hash,-1,sizeof(hash));
sum = 0;
while(n--)
{
key = 0;
for(int i=0; i<6; i++)
{
int t;
scanf("%d",&t);
key = ( key + t )%Prime;
x.data[i] = t;
}
if(!flag)//如果沒找到
{
for(int i=hash[key]; i!=-1; i=e[i].next)
{
node t=e[i];
for(int j=0; j<6; j++)
{
judge = true;
for(int k=0; k<6; k++)
if(t.data[k]!=x.data[(5-j-k+6)%6])
{
judge=false;
break;
}
if( judge )
flag = judge;
}
for(int j=0; j<6; j++)
{
judge = true;
for(int k=0; k<6; k++)
if(t.data[k]!=x.data[(j+k)%6])
{
judge=false;
break;
}
if( judge )
flag = judge;
}
}
sum++;//記下雪花個數
e[ sum ] = x;//儲存雪花資料
e[ sum ].next = hash[key];//雪花的下一個被賦值為雜湊表對應的頭,尾插入,參考鏈地址法的插入
hash[key] = sum;//當前雪花取代雜湊連結串列頭
}
}
if(flag)
cout<<"Twin snowflakes found."<<endl;
else
cout<<"No two snowflakes are alike."<<endl;
return 0;
}
POJ 2503
題目大意:給出兩個字串,前者為後者的翻譯,這樣的字串有多組,輸入多組作為詞典,之後再輸出多個字串,輸出其翻譯,如果不存在輸出對應資訊
思路:因為沒有給出確切的字串組數,所以只能一行行讀,然後進行分割,之後用map儲存且查詢即可,map的find函式效率高於[]符號
程式碼
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
map<string,string>Dictionary;
map<string,string>::iterator p;
string output,input;
int main()
{
string line="";
while(1)
{
getline(cin,line);
if(line=="")break;
int pos=line.find(' ');
output=line.substr(0,pos);
input=line.substr(pos+1,line.rfind(' ')-pos-1);
Dictionary[input]=output;
}
char word[300];
while(scanf("%s",word)!=EOF)
{
if(Dictionary.find(word)!=Dictionary.end())
cout <<Dictionary[word];
else
printf("eh");
putchar('\n');
}
return 0;
}
總結
本週的題目較為基礎,但是模擬賽的習題還是挺有難度,字串部分需要整理好Weekly 5部分後加強練習