A Great Alchemist

来源:互联网 发布:办公考勤软件 编辑:程序博客网 时间:2024/05/22 17:30

A Great Alchemist


Time limit : 2sec / Stack limit : 256MB / Memory limit : 256MB

Problem

Carol is a great alchemist.

In her world, each metal has a name of 2N (N is an integer) letters long, which consists of uppercase alphabets.

Carol can create metal S3 from S1 and S2 alchemical when she can make the name of S3 by taking N letters each from S1 and S2then rearranging them properly.

You are given 3 names of the metal S1S2S3. Determine wether Carol can create S3 from S1 and S2 or not.


Input

The input will be given in the following format from the Standard Input.

S1S2S3
  • On the first line, you will be given the name of the first metal material S1.
  • On the second line, you will be given the name of the second metal material S2.
  • On the third line, you will be given the name of the metal S3, which Carol wants to create.
  • Each character in the S1S2, and S3 will be an uppercase English alphabet letter.
  • Each string S1S2 and S3 has same number of letters and the number is always even.
  • It is guaranteed that 2≦|S1|≦105

Output

If Carol can create S3 from S1 and S2, output YES, if not, output NO in one line. Make sure to insert a line break at the end of the output.


Input Example 1

  1. AABCCD
  2. ABEDDA
  3. EDDAAA

Output Example 1

  1. YES

You can make EDDAAA by picking AAD from the first metal, and AED from the second metal.


Input Example 2

  1. AAAAAB
  2. CCCCCB
  3. AAABCB

Output Example 2

  1. NO

To make AAABCB, you have to take at least four letters from the first material. So this can't be created alchemical.


这题为实验室阿呆师弟在线笔试的一道题目。


1. 最初拿到题目的时候,由于时间有限,用最直接的暴力方法求解,递归过程中剪枝,代码如下

#include<iostream>#include<cstring>#include<string>using namespace std;bool get(int* cnt1, int* cnt2,int* cnt3,int index,int c1,int c2,int N){    if(index==26)        return c1==c2 && c1==N;    if(c1>N||c2>N)        return false;    if(cnt1[index]+cnt2[index]<cnt3[index])        return false;    if(cnt1[index]+cnt2[index]==cnt3[index])        return get(cnt1,cnt2,cnt3,index+1,c1+cnt1[index],c2+cnt2[index],N);    for(int i = 0; i <= cnt1[index] && cnt3[index]>=i;i++)    {        if(c1+i>N)            return false;        if(cnt3[index]-i > cnt2[index])            continue;        if(get(cnt1,cnt2,cnt3,index+1,c1+i,c2+cnt3[index]-i,N))            return true;    }    return false;}int main(int argc, char* argv[]){    char s1[256];    char s2[256];    char s3[256];    cin>>s1;    cin>>s2;    cin>>s3;    int N = strlen(s1)/2;    int cnt1[26];    int cnt2[26];    int cnt3[26];    for(int i = 0; i < 26; i++)    {        cnt1[i] = cnt2[i] = cnt3[i] = 0;    }    for(int i = 0; i < strlen(s1);i++)    {        cnt1[s1[i]-'A']++;        cnt2[s2[i]-'A']++;        cnt3[s3[i]-'A']++;    }    if(get(cnt1,cnt2,cnt3,0,0,0,N))    {        cout<<"YES"<<endl;    }    else        cout<<"NO"<<endl;    return 0;}

暴力方法粗暴,但是直观,提交后Time Limit Exceeded. 

2. 进一步分析发现在暴力递归过程中会有一些已经计算过的状态再次计算,尝试加入历史记录,增加剪枝条件。

#include<iostream>#include<cstring>#include<string>using namespace std;bool get(int* cnt1, int* cnt2,int* cnt3,int index,int c1,int c2,int N,bool** his){    if(index==26)        return c1==c2 && c1==N;    if(c1>N||c2>N)        return false;    if(his[index][c1]==false)        return false;    if(cnt1[index]+cnt2[index]==cnt3[index])    {        his[index][c1] = get(cnt1,cnt2,cnt3,index+1,c1+cnt1[index],c2+cnt2[index],N,his);        return his[index][c1];    }    for(int i = 0; i <= cnt1[index] && cnt3[index]>=i;i++)    {        if(c1+i>N)            return false;        if(cnt3[index]-i > cnt2[index])            continue;        if(get(cnt1,cnt2,cnt3,index+1,c1+i,c2+cnt3[index]-i,N,his))            return true;    }    his[index][c1] = false;    return false;}int main(int argc, char* argv[]){    char* s1 = new char[102400];    char* s2 = new char[102400];    char* s3 = new char[102400];    cin>>s1;    cin>>s2;    cin>>s3;    int N = strlen(s1)/2;    int cnt1[26];    int cnt2[26];    int cnt3[26];    bool* his[26];    for(int i = 0; i < 26; i++)    {        cnt1[i] = cnt2[i] = cnt3[i] = 0;    }    for(int i = 0; i < strlen(s1);i++)    {        cnt1[s1[i]-'A']++;        cnt2[s2[i]-'A']++;        cnt3[s3[i]-'A']++;    }    for(int i = 0; i < 26; i++)    {        if(cnt1[i] + cnt2[i] < cnt3[i])        {            cout<<"NO"<<endl;            return false;        }    }    for(int i = 0; i < 26;i++)    {        his[i] = new bool[N+1];        for(int j = 0; j < N+1; j++)        {            his[i][j] = true;        }    }    if(get(cnt1,cnt2,cnt3,0,0,0,N,his))    {        cout<<"YES"<<endl;    }    else        cout<<"NO"<<endl;    return 0;}

提交后大部分测试用例通过,小部分Time Limit Exceeded. 代码应该还有可优化的空间,不过这时候答题时间已经过了。


3. 阿呆说可以用dp求解,不过直观上感觉这道题应该不是dp的路子。重新分析题目后,发现在组合第三个字符串的时候,如果先尽量从第一个字符串里面取字符的话,这时候第一个字符串使用的字符数total1会大于N,这时候如果可以尝试把total1调节到N,那么就可以输出YES,否则就是NO。

    这里在调节的时候有一个trick,就是字符A-Z中,无论调节哪一个字符对最终结果都没有影响。为了理解这个过程,我们把字符串组合问题抽象成两个数组A[26]和B[26],分别表示第一个字符串和第二个字符串所使用的字符数。我们希望sum(A)==N并且sum(B)==N,A数组中任意一个element-1,所对应的B数组中的element+1,我们希望通过不断的减少A数组中某些元素的取值,而使得sum(A)==N。这里比较方便的一点就是A数组中的每一个元素在数值减少的时候,可以减少1、2、3...所有合理的数组(是一个连续的过程)。

     根据这个思路,可以得到如下代码:

#include<iostream>#include<cstring>#include<string>using namespace std;int main(int argc, char* argv[]){    char* s1 = new char[102400];    char* s2 = new char[102400];    char* s3 = new char[102400];    cin>>s1;    cin>>s2;    cin>>s3;    int N = strlen(s1)/2;    int cnt1[26];    int cnt2[26];    int cnt3[26];    int use1[26];    int use2[26];    bool* his[26];    for(int i = 0; i < 26; i++)    {        cnt1[i] = cnt2[i] = cnt3[i] = 0;        use1[i] = use2[i] = 0;    }    for(int i = 0; i < strlen(s1);i++)    {        cnt1[s1[i]-'A']++;        cnt2[s2[i]-'A']++;        cnt3[s3[i]-'A']++;    }    int total1 = 0;    for(int i = 0; i < 26; i++)    {        if(cnt1[i] + cnt2[i] < cnt3[i]){cout<<"NO"<<endl;        return 0;}        if(cnt1[i] >= cnt3[i])            use1[i] = cnt3[i];        else            use1[i] = cnt1[i];        total1 += use1[i];        use2[i] = cnt3[i] - use1[i];    }    if(total1<N){        cout<<"NO"<<endl;        return 0;}    bool bminus = true;    for(int i = 0; i < 26; i++)    {        while(total1> N && use1[i] > 0 && use2[i] < cnt2[i])        {            use1[i]--;            use2[i]++;            total1--;        }        if(total1==N)        {            cout<<"YES"<<endl;            return 0;        }    }    if(total1==N)    {        cout<<"YES"<<endl;    }    else        cout<<"NO"<<endl;    return 0;}

这是修改后的最终版代码,提交后AC。


4. 随着这个思路,再进一步分析会发现,total1的数值在调整的过程中是一个线性减小的过程,如果我们再计算一个total2,表示尽可能少的从第一个字符串取字符的情况下,字符串1所使用的字符数,那么如果total1 > N > total2 或者total1== N || total2==N,那么total1就必然能够调整到N,也就可以得到结果。所以可以得到如下更精简的代码:

#include<iostream>#include<cstring>#include<string>using namespace std;int main(int argc, char* argv[]){char* s1 = new char[102400];char* s2 = new char[102400];char* s3 = new char[102400];cin>>s1;cin>>s2;cin>>s3;int N = strlen(s1)/2;int cnt1[26];int cnt2[26];int cnt3[26];int use1,use2;for(int i = 0; i < 26; i++){cnt1[i] = cnt2[i] = cnt3[i] = 0;}for(int i = 0; i < strlen(s1);i++){cnt1[s1[i]-'A']++;cnt2[s2[i]-'A']++;cnt3[s3[i]-'A']++;}int total1 = 0, total2=0;for(int i = 0; i < 26; i++){if(cnt1[i] + cnt2[i] < cnt3[i]){cout<<"NO"<<endl;return false;}if(cnt1[i] >= cnt3[i])total1 += cnt3[i];elsetotal1 += cnt1[i]; //calculate total2if(cnt2[i] >= cnt3[i])total2 += 0;elsetotal2 += cnt3[i]-cnt2[i];}if(total1 > N && total2 < N)cout<<"YES"<<endl;else if(total1 == N || total2 == N)cout<<"YES"<<endl;else cout<<"NO"<<endl;return 0;}
提交后AC。

0 0
原创粉丝点击