1. 程式人生 > 其它 >隊內訓練第三週

隊內訓練第三週

技術標籤:快樂寒假字串演算法資料結構雜湊表

隊內訓練第三週

涉及的知識點

本週練習主要涉及字串

已完成題目 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部分後加強練習